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 | ||||
| #      maintained by : Niels G. W. Serup <ns@metanohi.name> | ||||
| 
 | ||||
| """ | ||||
| Colour boxes. | ||||
| """ | ||||
| 
 | ||||
| import random | ||||
| 
 | ||||
| def generate_colour_boxes(nwells, nboxes): | ||||
|  | ||||
| @ -53,9 +53,7 @@ class Left(Direction): | ||||
|         x, y = pos | ||||
|         return x - 1, y | ||||
| 
 | ||||
| all_directions = set((Up, Left, Down, Right)) | ||||
| 
 | ||||
| succ = lambda d: all_directions[(all_directions.index(d) + 1) % 4] | ||||
| pred = lambda d: all_directions[(all_directions.index(d) - 1) % 4] | ||||
| 
 | ||||
| isDirection = lambda obj: obj in (Up, Left, Down, Right) | ||||
| all_directions = (Up, Right, Down, Left) | ||||
| _sp = lambda n: lambda d: all_directions[(all_directions.index(d) + n) % 4] | ||||
| succ, pred = _sp(1), _sp(-1) | ||||
| isDirection = all_directions.__contains__ | ||||
|  | ||||
							
								
								
									
										116
									
								
								robotgame/logic/lasermirror.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								robotgame/logic/lasermirror.py
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||
| import math | ||||
| import random | ||||
| import itertools | ||||
| from robotgame.logic.direction import * | ||||
| import random | ||||
| 
 | ||||
| class Blocker(object): | ||||
|     pass | ||||
| @ -70,7 +70,8 @@ def reaches_goal(playfield, width, height, max_steps, start_pos, goal_pos): | ||||
|     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: | ||||
|       * 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 has at most nstones stones | ||||
| 
 | ||||
|     Return (playfield : {(x, y): Direction | Blocker}, | ||||
|     Return (playfield : {(x, y): Direction | Blocker | None}, | ||||
|             steps : 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) | ||||
|         min_width, min_height = _min_play_size(nturns) | ||||
| 
 | ||||
|     do_transpose = random.choice((True, False)) | ||||
|     if do_transpose is None: | ||||
|         do_transpose = random.choice((True, False)) | ||||
|     if do_transpose: | ||||
|         width, height = height, width | ||||
| 
 | ||||
|     turns, stones = [((0, 0), None)], [] | ||||
|     turns = [((0, 0), None)] | ||||
|     stones = [] | ||||
|     x, y = (0, 0) | ||||
|     not_allowed_y = [] | ||||
|     offset_x = 0 | ||||
| @ -110,6 +113,7 @@ def generate_simple_playfield(width, height, nturns, nstones): | ||||
|             turns.append(((x, height - 1), Right)) | ||||
|             break | ||||
|         elif missing == 0: | ||||
|             turns[-1] = ((width - 1, turns[-1][0][1]), Down) | ||||
|             break | ||||
|         else: | ||||
|             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)) | ||||
|             x, y = x1, y1 | ||||
|     turns.append(((width - 1, height - 1), None)) | ||||
|     if not start_inside: | ||||
|         del turns[0] | ||||
| 
 | ||||
|     if do_transpose: | ||||
|         turns[:] = [((y, x), { | ||||
| @ -181,7 +187,7 @@ def print_playfield(playfield, width, height, hide_directions): | ||||
|     for (x, y), val in playfield.items(): | ||||
|         if isDirection(val) and hide_directions: | ||||
|             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' | ||||
|     print('\n'.join(''.join(line) for line in text)) | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										15
									
								
								tests/lasermirror_tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								tests/lasermirror_tests.py
									
									
									
									
									
										Normal file
									
								
							| @ -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( | ||||
|             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__': | ||||
|     unittest.main() | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Niels Serup
						Niels Serup