Fixed a few bugs and added laser mirror room generator.
This commit is contained in:
parent
20e06e572e
commit
7666891baa
|
@ -23,6 +23,10 @@
|
||||||
# copyright : (C) 2012 Niels G. W. Serup
|
# copyright : (C) 2012 Niels G. W. Serup
|
||||||
# maintained by : Niels G. W. Serup <ns@metanohi.name>
|
# maintained by : Niels G. W. Serup <ns@metanohi.name>
|
||||||
|
|
||||||
|
"""
|
||||||
|
Colour boxes.
|
||||||
|
"""
|
||||||
|
|
||||||
import random
|
import random
|
||||||
|
|
||||||
def generate_colour_boxes(nwells, nboxes):
|
def generate_colour_boxes(nwells, nboxes):
|
||||||
|
|
|
@ -53,9 +53,7 @@ class Left(Direction):
|
||||||
x, y = pos
|
x, y = pos
|
||||||
return x - 1, y
|
return x - 1, y
|
||||||
|
|
||||||
all_directions = set((Up, Left, Down, Right))
|
all_directions = (Up, Right, Down, Left)
|
||||||
|
_sp = lambda n: lambda d: all_directions[(all_directions.index(d) + n) % 4]
|
||||||
succ = lambda d: all_directions[(all_directions.index(d) + 1) % 4]
|
succ, pred = _sp(1), _sp(-1)
|
||||||
pred = lambda d: all_directions[(all_directions.index(d) - 1) % 4]
|
isDirection = all_directions.__contains__
|
||||||
|
|
||||||
isDirection = lambda obj: obj in (Up, Left, Down, Right)
|
|
||||||
|
|
|
@ -0,0 +1,116 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# 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 <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' '
|
||||||
|
#
|
||||||
|
# lasermirror.py
|
||||||
|
# --------------------
|
||||||
|
# date created : Tue Aug 7 2012
|
||||||
|
# copyright : (C) 2012 Niels G. W. Serup
|
||||||
|
# maintained by : Niels G. W. Serup <ns@metanohi.name>
|
||||||
|
|
||||||
|
"""
|
||||||
|
Management of lasers in rooms of mirrors and targets.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import math
|
||||||
|
import random
|
||||||
|
import itertools
|
||||||
|
from robotgame.logic.direction import *
|
||||||
|
import robotgame.logic.rollingstone as rstone
|
||||||
|
|
||||||
|
class Mirror(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Lever(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Target(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def generate_simple_playfield(nmirrors):
|
||||||
|
"""
|
||||||
|
Generate a completable 12x12 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
|
||||||
|
* there are four laser targets
|
||||||
|
* there are nmirrors mirrors
|
||||||
|
* there are nmirrors levers
|
||||||
|
* all levers are at the wall
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
succs = lambda d: d
|
||||||
|
source_direc = Up
|
||||||
|
nlevers = nmirrors
|
||||||
|
for missing in range(4, 0, -1):
|
||||||
|
nm = nmirrors / missing
|
||||||
|
nmirrors -= nm
|
||||||
|
stone_playfield, _ = rstone.generate_simple_playfield(
|
||||||
|
5, 5, 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
|
||||||
|
succs = (lambda s: lambda d: succ(s(d)))(succs)
|
||||||
|
source_direc = succ(source_direc)
|
||||||
|
|
||||||
|
emptys = set(itertools.product(range(12), range(12))) \
|
||||||
|
- set(playfield.keys())
|
||||||
|
emptys = set([(0, y) for y in range(12)]
|
||||||
|
+ [(11, y) for y in range(12)]
|
||||||
|
+ [(x, 0) for x in range(12)]
|
||||||
|
+ [(x, 11) for x in range(12)])
|
||||||
|
for _ in range(nlevers):
|
||||||
|
if not emptys:
|
||||||
|
raise Exception("Not enough space for all levers!")
|
||||||
|
pos = random.choice(list(emptys))
|
||||||
|
playfield[pos] = Lever
|
||||||
|
emptys.remove(pos)
|
||||||
|
return playfield
|
||||||
|
|
||||||
|
def _adjust(source_direc, w, h, x, y):
|
||||||
|
return {
|
||||||
|
Up: lambda x, y: (x, y),
|
||||||
|
Right: lambda x, y: (w - y, x),
|
||||||
|
Down: lambda x, y: (w - x, h - y),
|
||||||
|
Left: lambda x, y: (y, h - x),
|
||||||
|
}[source_direc](x, y)
|
||||||
|
|
||||||
|
def print_playfield(playfield, width, height, hide_directions=False):
|
||||||
|
text = [['·' for _ in range(width)] for _ in range(height)]
|
||||||
|
for (x, y), val in playfield.items():
|
||||||
|
if isDirection(val) and hide_directions:
|
||||||
|
continue
|
||||||
|
text[y][x] = '%' if val is rstone.Blocker \
|
||||||
|
else 'x' if val is Mirror \
|
||||||
|
else 'L' if val is Lever \
|
||||||
|
else 'T' if val is Target else 'N'
|
||||||
|
print('\n'.join(''.join(line) for line in text))
|
||||||
|
|
|
@ -30,9 +30,9 @@ direction-changing turns. Also has a pseudo-random playfield generator.
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
import math
|
import math
|
||||||
|
import random
|
||||||
import itertools
|
import itertools
|
||||||
from robotgame.logic.direction import *
|
from robotgame.logic.direction import *
|
||||||
import random
|
|
||||||
|
|
||||||
class Blocker(object):
|
class Blocker(object):
|
||||||
pass
|
pass
|
||||||
|
@ -70,7 +70,8 @@ def reaches_goal(playfield, width, height, max_steps, start_pos, goal_pos):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def generate_simple_playfield(width, height, nturns, nstones):
|
def generate_simple_playfield(width, height, nturns, nstones,
|
||||||
|
do_transpose=None, start_inside=True):
|
||||||
"""
|
"""
|
||||||
Generate a completable playfield where:
|
Generate a completable playfield where:
|
||||||
* the starting position is in the upper left corner
|
* the starting position is in the upper left corner
|
||||||
|
@ -78,7 +79,7 @@ def generate_simple_playfield(width, height, nturns, nstones):
|
||||||
* the playfield is completable in nturns or less
|
* the playfield is completable in nturns or less
|
||||||
* the playfield has at most nstones stones
|
* the playfield has at most nstones stones
|
||||||
|
|
||||||
Return (playfield : {(x, y): Direction | Blocker},
|
Return (playfield : {(x, y): Direction | Blocker | None},
|
||||||
steps : int)
|
steps : int)
|
||||||
where (x, y) : (int, int)
|
where (x, y) : (int, int)
|
||||||
|
|
||||||
|
@ -95,11 +96,13 @@ def generate_simple_playfield(width, height, nturns, nstones):
|
||||||
nturns = min(2 * (width - 1), 2 * (height - 1) - 1)
|
nturns = min(2 * (width - 1), 2 * (height - 1) - 1)
|
||||||
min_width, min_height = _min_play_size(nturns)
|
min_width, min_height = _min_play_size(nturns)
|
||||||
|
|
||||||
|
if do_transpose is None:
|
||||||
do_transpose = random.choice((True, False))
|
do_transpose = random.choice((True, False))
|
||||||
if do_transpose:
|
if do_transpose:
|
||||||
width, height = height, width
|
width, height = height, width
|
||||||
|
|
||||||
turns, stones = [((0, 0), None)], []
|
turns = [((0, 0), None)]
|
||||||
|
stones = []
|
||||||
x, y = (0, 0)
|
x, y = (0, 0)
|
||||||
not_allowed_y = []
|
not_allowed_y = []
|
||||||
offset_x = 0
|
offset_x = 0
|
||||||
|
@ -110,6 +113,7 @@ def generate_simple_playfield(width, height, nturns, nstones):
|
||||||
turns.append(((x, height - 1), Right))
|
turns.append(((x, height - 1), Right))
|
||||||
break
|
break
|
||||||
elif missing == 0:
|
elif missing == 0:
|
||||||
|
turns[-1] = ((width - 1, turns[-1][0][1]), Down)
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
allowed = set(range(0, height)) - set(not_allowed_y)
|
allowed = set(range(0, height)) - set(not_allowed_y)
|
||||||
|
@ -129,6 +133,8 @@ def generate_simple_playfield(width, height, nturns, nstones):
|
||||||
turns.append(((x1, y1), None))
|
turns.append(((x1, y1), None))
|
||||||
x, y = x1, y1
|
x, y = x1, y1
|
||||||
turns.append(((width - 1, height - 1), None))
|
turns.append(((width - 1, height - 1), None))
|
||||||
|
if not start_inside:
|
||||||
|
del turns[0]
|
||||||
|
|
||||||
if do_transpose:
|
if do_transpose:
|
||||||
turns[:] = [((y, x), {
|
turns[:] = [((y, x), {
|
||||||
|
@ -181,7 +187,7 @@ def print_playfield(playfield, width, height, hide_directions):
|
||||||
for (x, y), val in playfield.items():
|
for (x, y), val in playfield.items():
|
||||||
if isDirection(val) and hide_directions:
|
if isDirection(val) and hide_directions:
|
||||||
continue
|
continue
|
||||||
text[y][x] = '%' if val == Blocker else repr(val).rsplit('.', 1)[1][0] \
|
text[y][x] = '%' if val is Blocker else repr(val).rsplit('.', 1)[1][0] \
|
||||||
if isDirection(val) else 'G'
|
if isDirection(val) else 'G'
|
||||||
print('\n'.join(''.join(line) for line in text))
|
print('\n'.join(''.join(line) for line in text))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
import unittest
|
||||||
|
from robotgame.logic.lasermirror import *
|
||||||
|
from robotgame.logic.direction import *
|
||||||
|
|
||||||
|
|
||||||
|
class LaserMirrorTest(unittest.TestCase):
|
||||||
|
def test_playfield_generation(self):
|
||||||
|
print()
|
||||||
|
playfield = generate_simple_playfield(13)
|
||||||
|
print_playfield(playfield, 12, 12)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -35,5 +35,11 @@ class RollingStoneTest(unittest.TestCase):
|
||||||
self.assertTrue(
|
self.assertTrue(
|
||||||
reaches_goal(playfield, 10, 10, steps, (0, 0), (9, 9)))
|
reaches_goal(playfield, 10, 10, steps, (0, 0), (9, 9)))
|
||||||
|
|
||||||
|
print()
|
||||||
|
playfield, steps = generate_simple_playfield(10, 10, 4, 11)
|
||||||
|
print_playfield(playfield, 10, 10, True)
|
||||||
|
self.assertTrue(
|
||||||
|
reaches_goal(playfield, 10, 10, steps, (0, 0), (9, 9)))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
Loading…
Reference in New Issue