diff --git a/README.txt b/README.txt
index 94fc798..652eef4 100644
--- a/README.txt
+++ b/README.txt
@@ -1 +1,8 @@
This is a good game.
+
+
+The rolling boulder task in level 1 is not guaranteed to be solvable, because
+you might not be able to move an arrow block to the position you need to move
+it to. However, this scenario is so unlikely that we have chosen to ignore it.
+
+
diff --git a/robotgame/level4.py b/robotgame/level4.py
new file mode 100644
index 0000000..741eb67
--- /dev/null
+++ b/robotgame/level4.py
@@ -0,0 +1,94 @@
+# This file is part of ROBOTGAME
+#
+# ROBOTGAME is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# ROBOTGAME is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# ROBOTGAME. If not, see .
+#
+# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
+#
+# level1.py
+# --------------------
+# date created : Fri Aug 10 2012
+# copyright : (C) 2012 Niels G. W. Serup
+# maintained by : Niels G. W. Serup
+
+"""
+The fourth level.
+"""
+
+import os
+import pygame
+import random
+import re
+import itertools
+
+import level
+import player
+import tile
+import block
+import boulder
+import lever
+import mirror
+import trigger
+import misc
+import worldobject
+
+import logic.lasermirror as lm
+
+class Level4(level.Level):
+ def __init__(self, game, graphics_dir, paused=False):
+ level.Level.__init__(self, game, graphics_dir,
+ size=(64 * 16, 48 * 16), paused=paused)
+
+ self.dimensions = 16, 16
+
+ for i in range(self.dimensions[0]):
+ for j in range(self.dimensions[1]):
+ self.tiles.append(
+ tile.Tile(self, i*64, (j+1)*48,
+ self.imgs['indoor%d' % random.randint(1, 6)]))
+
+ self.draw_background()
+
+ playfield = lm.generate_simple_playfield(16)
+
+ for (x, y), t in playfield.items():
+ x, y = 64 * x, 48 * (y + 1)
+ self.objects.append({
+ lm.Mirror: mirror.Mirror(self, x, y, random.choice((True, False))),
+ lm.Lever: lever.Lever(
+ self, x, y, [], toggling=True,
+ anim='lever_leftright' if x == 0 or x == 15 * 64
+ else 'lever_updown'),
+ lm.Target: block.Block(self, x, y, self.imgs['block3'],
+ movable=False),
+ lm.Blocker: block.Block(self, x, y, self.imgs['block1'],
+ movable=False)
+ }[t])
+ mirrors = list(filter(lambda obj: isinstance(obj, mirror.Mirror),
+ self.objects))
+ levers = list(filter(lambda obj: isinstance(obj, lever.Lever),
+ self.objects))
+ random.shuffle(levers)
+ for l in levers:
+ m = min(mirrors, key=lambda m: misc.manhattan_dist(
+ (m.x, m.y), (l.x, l.y)))
+ mirrors.remove(m)
+ l.links.append((lambda m: lambda setting: m.rotate())(m))
+
+ self.player.set_pos(64 * 7, 48 * 2)
+ self.player.set_init_pos()
+
+
+ def restart(self):
+ for obj in self.objects:
+ obj.reset_pos()
+
diff --git a/robotgame/loader.py b/robotgame/loader.py
index 588564e..2967072 100644
--- a/robotgame/loader.py
+++ b/robotgame/loader.py
@@ -85,7 +85,9 @@ class Loader(object):
('arrow_down', os.path.join('matt', 'down')),
('arrow_left', os.path.join('matt', 'right')),
- ('stairs', 'stairs')]
+ ('stairs', 'stairs'),
+
+ ('mirror', 'mirror')]
):
self.imgs[anim] = []
diff --git a/robotgame/logic/lasermirror.py b/robotgame/logic/lasermirror.py
index 3c59c8c..3371b97 100644
--- a/robotgame/logic/lasermirror.py
+++ b/robotgame/logic/lasermirror.py
@@ -19,8 +19,8 @@
#
# lasermirror.py
# --------------------
-# date created : Tue Aug 7 2012
-# copyright : (C) 2012 Niels G. W. Serup
+# date created : Tue Aug 7 2016
+# copyright : (C) 2016 Niels G. W. Serup
# maintained by : Niels G. W. Serup
"""
@@ -33,6 +33,7 @@ import random
import itertools
from robotgame.logic.direction import *
import robotgame.logic.rollingstone as rstone
+from robotgame.logic.rollingstone import Blocker
import robotgame.misc as misc
class Mirror(object):
@@ -46,12 +47,12 @@ class Target(object):
def generate_simple_playfield(nmirrors):
"""
- Generate a completable 12x12 playfield where:
+ Generate a completable 16x16 playfield where:
* there are four laser sources, one in each corner
+ the one in the upper left corner (0, 0) starts in (0, -1) heading down
- + the one in the upper right corner (11, 0) starts in (12, 0), heading left
- + the one in the lower right corner (11, 11) starts in (11, 12), heading up
- + the one in the lower left corner (0, 11) starts in (-1, 11), heading right
+ + the one in the upper right corner (15, 0) starts in (16, 0), heading left
+ + the one in the lower right corner (15, 15) starts in (15, 16), heading up
+ + the one in the lower left corner (0, 15) starts in (-1, 15), heading right
* there are four laser targets
* there are nmirrors mirrors
* there are nmirrors levers
@@ -59,14 +60,14 @@ def generate_simple_playfield(nmirrors):
Return playfield : {(x, y): Target | Mirror | rstone.Blocker | Lever}
"""
- playfield = {(4, 4): Target,
- (7, 4): Target,
- (4, 7): Target,
- (7, 7): Target,
- (5, 5): rstone.Blocker,
- (5, 6): rstone.Blocker,
- (6, 5): rstone.Blocker,
- (6, 6): rstone.Blocker,
+ playfield = {(6, 6): Target,
+ (9, 6): Target,
+ (6, 9): Target,
+ (9, 9): Target,
+ (7, 7): rstone.Blocker,
+ (7, 8): rstone.Blocker,
+ (8, 7): rstone.Blocker,
+ (8, 8): rstone.Blocker,
}
succs = lambda d: d
source_direc = Up
@@ -75,23 +76,21 @@ def generate_simple_playfield(nmirrors):
nm = nmirrors / missing
nmirrors -= nm
stone_playfield, _ = rstone.generate_simple_playfield(
- 5, 5, nm, 0, False, False)
+ 7, 7, nm, 0, False, False)
for pos, direc in stone_playfield.items():
- if direc is not None and pos >= (0, 0):
- playfield[_adjust(source_direc, 12 - 1, 12 - 1, *pos)] = Mirror
+ playfield[_adjust(source_direc, 16 - 1, 16 - 1, *pos)] = Mirror
succs = (lambda s: lambda d: succ(s(d)))(succs)
source_direc = succ(source_direc)
- occup = set(playfield.keys())
- emptys = list(
- set([(0, y) for y in filter(lambda y: (1, y) not in occup, range(12))]
- + [(11, y) for y in filter(lambda y: (10, y) not in occup, range(12))]
- + [(x, 0) for x in filter(lambda x: (x, 1) not in occup, range(12))]
- + [(x, 11) for x in filter(lambda x: (x, 10) not in occup, range(12))])
- - occup)
- if len(emptys) < nlevers:
- raise Exception("Not enough space for all levers!")
- for pos in misc.pick_random_elements(emptys, nlevers):
+ for _ in range(nlevers):
+ # This needs to be optimized...
+ occup = set(playfield.keys())
+ emptys = list(
+ set([(0, y) for y in filter(lambda y: (1, y) not in occup, range(16))]
+ + [(15, y) for y in filter(lambda y: (10, y) not in occup, range(16))]
+ + [(x, 0) for x in filter(lambda x: (x, 1) not in occup, range(16))]
+ + [(x, 15) for x in filter(lambda x: (x, 10) not in occup, range(16))]) - occup)
+ pos = emptys[random.randrange(len(emptys))]
playfield[pos] = Lever
return playfield
diff --git a/robotgame/logic/rollingstone.py b/robotgame/logic/rollingstone.py
index 75b5ca9..fd0831c 100644
--- a/robotgame/logic/rollingstone.py
+++ b/robotgame/logic/rollingstone.py
@@ -123,7 +123,7 @@ def generate_simple_playfield(width, height, nturns, nstones,
break
else:
allowed = set(range(0, height)) - set(not_allowed_y)
- if missing == 3:
+ if missing <= 3:
allowed -= set((height - 1,))
if missing == nturns:
allowed -= set((0,))
diff --git a/robotgame/mirror.py b/robotgame/mirror.py
new file mode 100644
index 0000000..5d2f867
--- /dev/null
+++ b/robotgame/mirror.py
@@ -0,0 +1,63 @@
+# This file is part of ROBOTGAME
+#
+# ROBOTGAME is free software: you can redistribute it and/or modify it under the
+# terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+#
+# ROBOTGAME is distributed in the hope that it will be useful, but WITHOUT ANY
+# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+# A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# ROBOTGAME. If not, see .
+#
+# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
+#
+# mirror.py
+# --------------------
+# date created : Fri Aug 10 2012
+# copyright : (C) 2012 Niels G. W. Serup
+# maintained by : Niels G. W. Serup
+
+"""
+A generic mirror for drawing.
+"""
+
+import pygame
+
+import worldobject
+
+class Mirror(worldobject.WorldObject):
+ def __init__(self, level, x, y, left_up=True):
+ self.__dict__.update(locals())
+ worldobject.WorldObject.__init__(self, level, x, y)
+
+ self.in_rotation = False
+
+ self.frame = 7
+ self.anim_speed = 12
+ self._half_anim_len = len(self.level.imgs['mirror']) / 2
+
+ def rotate(self):
+ if self.in_rotation:
+ return
+ self.in_rotation = True
+ self.left_up = not self.left_up
+ self.frame = 0
+
+ def update(self, e, t, dt):
+ if self.in_rotation:
+ top = self._half_anim_len - 1
+ self.frame = min(self.frame + self.anim_speed * dt, top)
+ if self.frame == top:
+ self.in_rotation = False
+
+ worldobject.WorldObject.update(self, e, t, dt)
+
+ def draw(self, window):
+ fn = self.frame + 1 if not self.left_up else self.frame + self._half_anim_len
+ self.img = self.level.imgs['mirror'][int(fn)]
+ window.blit(self.img, (self.x - 32 - self.level.camera_x,
+ self.y - self.img.get_size()[1] + 24
+ - self.level.camera_y))
diff --git a/robotgame/misc.py b/robotgame/misc.py
index 04547a3..038b629 100644
--- a/robotgame/misc.py
+++ b/robotgame/misc.py
@@ -7,3 +7,7 @@ def pick_random_elements(xs, n):
yield xs[i1]
xs[i1] = xs[i]
+def manhattan_dist(p0, p1):
+ x0, y0 = p0
+ x1, y1 = p1
+ return abs(x1 - x0) + abs(y1 - y0)
diff --git a/tests/lasermirror_tests.py b/tests/lasermirror_tests.py
index bda9483..bd40f24 100644
--- a/tests/lasermirror_tests.py
+++ b/tests/lasermirror_tests.py
@@ -8,7 +8,7 @@ from robotgame.logic.direction import *
class LaserMirrorTest(unittest.TestCase):
def test_playfield_generation(self):
print()
- playfield = generate_simple_playfield(13)
+ playfield = generate_simple_playfield(16)
print_playfield(playfield, 12, 12)
if __name__ == '__main__':