Time Weave

From DoctorWhen

The Pitch

In an early tests of the prototype Dr When did not supply enough power to the Heisenberg Compensator, the part of the machine responsible for dampending timestream-altering ripple effects.

As a result, when he returned to the present he found a world strangely altered in subtle ways.

He needs your help to recreate the fabric of time, suppressing events that shouldn't happen and enabling connections that should happen.

A correctly re-ordered timestream should provide a clue on the date of Dr When's next test and the correct amount of power to send to the HC.

The Puzzle

General Idea

(see attached bitmaps for sample pieces) Players get 10 strips and a board.

5 strips are 'rows', 5 are 'columns'. The strips are marked with 'paths'. Places where the strips should cross also contain historical "facts".

The board is a 5x5 grid, possibly with some extra rows/columns for spacing, and an extra row/column on either side.

The top and left of the grid contain cells with the names of famous people (5 top, 5 left). The people can be paired up into famous 'teams' or 'meetings': person X met person Y.

The puzzle is to assemble the strips, weaving them over and under each other, so that for each ((X, Y) met at Z), there is a path from X to Y passing through Z.


Expectation

Hopefully an 'ooh' when they see the tiles and paths (I really like playing with tiles/paths just to watch the lines move).

The first "Aha" is that some headlines are wrong (hopefully triggered by the few that are really wrong). This leads to an examination of all the facts, identifying which are right and which are wrong.

The second "Aha" is that since the task is to "put history right", all 'wrong' facts should be covered by a 'right' fact. This would profoundly limit the permutations of rows columns. I would design it so that, within that restriction, all rows but 2 are locked down (2 choices), and all columns but 3 (6 choices).

The first "reward" is you now have the reasonable search space of 12 possibilities to get the paths to work properly.

The second "reward" is that on a correctly re-assembled grid, if you add the digits of the years of the 'facts' and map to letters using simple A = 1, B = 2, etc., you get the message: "five gigawatts by mid may"

Addressing Most Recent Comments

In the latest playtest, people got the general idea but they did not recognize that some facts were false, so they were left thinking that the only solution was brute force.

Solutions:

1. Make sure they get directions that talk about altered time stream and setting facts right.

2. Added one row of clearly false facts, all grisly deaths taken from Gashlycrumb Tinies. Hopefully players will realize that these are false, and they conceptually belong together, so they will be well on their way to positioning some strips. Additionally, the dates on these GT clues map to the corresponding GT letter (e.g. in 2000 someone is devoured by bears, 2+0+0+0 = 2 = B = Basil, devoured by bears), offering an extra clue on how to get final message.

Attachments

* Media:Board.png
* Media:Rows.PNG
* Media:Cols.png

Code

  1. !python

import random import sys import copy import Image import ImageDraw

""" Tester for strip puzzle. """

CELL_SIZE = 160 LINE_WIDTH = 10 HASH_WIDTH = 2 HASH_LENGTH = 20


TrueFacts = [

   ["2004",
    "Summer Olympics in Athens"],
   ["1800",
    "Millard Fillmore born"],
   ["1993",
    "Ruth Bader Ginsberg",
    "appointed to Supreme",
    "Court"],
   ["2003",
    "Saddam Hussein captured",
    "by American Troops"],
   ["1989",
    "San Francisco Bay area",
    "earthquake measuring 7.1"],
   ["2005",
    "Hurricane Katrina wreaks",
    "catastrophic damage on ",
    "the Gulf coast"],
   ["1710",
    "Emperor Higashiyama of",
    "Japan dies"],
   ["2005",
    "Pope John Paul II Dies"],
   ["100",
    "Emperor Trajan and",
    "Sextus Julius Frontinus",
    "become Roman Consuls"],
   ["1985",
    "PLO terrorists hijack",
    "Achille Lauro"],
   ["1000",
    "Stephen I becomes King",
    "of Hungary"],
   ["1973",
    "Spiro T. Agnew resigns",
    "as Vice President"],
   ["1883",
    "LIFE magazine is founded"],
   ["1954",
    "Lord of the Flies is",
    "published"],
   ["1998",
    "Former Chilean dictator",
    "Augusto Pinochet",
    "arrested in London"],
   ["2000",
    "Vicente Fox Quesada",
    "elected president of",
    "Mexico"],
   ["1978",
    "John Paul I, 65, dies",
    "unexpectedly after 34",
    "days in office"],
   ["1899",
    "Felix Hoffmann patents",
    "aspirin"],
   ["1930",
    "Grant Wood paints ",
    "American Gothic."],
   ["1620",
    "Mysterious rain of",
    "frogs in Weil der",
    "Stadt"],
   
   ["2002",
    "East Timor becomes a",
    "new nation"],
   ["1989",
    "Exxon Valdez oil spill"],
   ["1822",
    "Coffee is no longer"
    "banned in Sweden"],
   ["1000",
    "Olaf I of Norway killed",
    "in battle"],
   ["1977",
    "South African activist",
    "Steve Biko dies in ",
    "police custody"]]

FalseFacts = [

   ["2001",
    "Shanghai wins the bid",
    "to host the 2008 Summer",
    "Olympics"],
   ["1955",
    "Alfred Hitchcock",
    "Presents TV program",
    "debuts on ABC-TV"],
   ["500",
    "Emperor Xuanwu becomes",
    "sovereign of the Qing",
    "Dynasty"],
   ["1988",
    "Hurricane Gladys",
    "devastates Jamaica"],
   ["1750",
    "Jose III takes over the",
    "throne of Portugal"],
   ["2000",
    "Farrah Fawcett is",
    "devoured by bears"],
   ["1890",
    "Horatio Alger, Jr.",
    "is consumed by a fire"],
   ["1970",
    "Howard Hughes sinks in",
    "a mire"],
   ["1250",
    "Erik Eriksson is done",
    "in by a thug"],
   ["1931",
    "Thomas Edison dies of",
    "ennui"],
   ["2007",
    "Luciano Pavarotti dies",
    "in a car accident"],
   ["1969",
    "The Soviet Union",
    "launches Soyuz 2"],
   ["1983",
    "The Greek part of ",
    "Cyprus declares ",
    "independence"],
   ["2005",
    "The Kashmir earthquake",
    "kills about 800 people"],
   ["2002",
    "Venus Williams defeats",
    "Serena Williams in the",
    "French Open"],
   ["1984",
    "John Napier Turner",
    "becomes Canada's 27th",
    "Prime Minister"],
   ["1855",
    "The Sigma Chi",
    "Fraternity is founded",
    "at Harvard University"],
   ["200",
    "Empress Sakura sends",
    "a Japanese fleet to",
    "invade Korea"],
   ["1010",
    "Emperor Murasaki writes",
    "The Tale of Genji in",
    "Japanese"],
   ["1831",
    "The Hunchback of Notre",
    "Dame is first published",
    "by Moliere"],
   
   ["1919",
    "The League of Nations",
    "is founded in Berlin"],
   ["2006",
    "The FIFA World Cup",
    "begins in France"],
   ["1985",
    "Rock Hudson dies of",
    "throat cancer"],
   ["1977",
    "American Roy Sullivan",
    "is struck by lightning",
    "for the twelfth time"],
   ["1000",
    "Leif Ericson lands in",
    "North America, calling",
    "it Jotunland"]]


SOLUTION = "five gigawatts by mid may"

  1. 0....5....0....5....0....5

TURN_TYPES = [

   '+',
   '\\',
   '/',
   ]

FWD_TURNS = {

   'E': 'N',
   'S': 'W',
   'W': 'S',
   'N': 'E',
   }

BACK_TURNS = {

   'E': 'S',
   'S': 'E',
   'W': 'N',
   'N': 'W',
   }

TURNS = """ + \ \ + + \ \ + / / + + \ / + \ + / \ \ \ / \ \ + """

VALUES = """ 0 1 1 0 1 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 1 1 1 0 1 """

def get_pattern_value(x, y):

   pieces = VALUES.split()
   return int(pieces[x + y * 5])

def get_turn_at(x, y, match):

   pieces = TURNS.split()
   val = pieces[x + y * 5]
   if match:
       return val
   else:
       while 1:
           test = random_turn()
           if test != val:
               return test
   

def hash_location(loc):

   return loc[0] * 10 + loc[1] * 100

def loc_string(loc):

   return "(" + str(loc[0]) + ", " + str(loc[1]) + ")"

def nth_letter(letters, index):

   letter = letters[index]
   if letter == ' ':
       return 0
   return ord(letter) - ord('a') + 1

def random_turn():

   index = random.randrange(0, 3)
   return TURN_TYPES[index]

def make_perms(array):

   if len(array) == 1:
       return [array]
   
   retval = []
   for v in array:
       cp = copy.deepcopy(array)
       cp.remove(v)
       arr = [v]
       perms = make_perms(cp)
       for perm in perms:
           perm.append(v)
           retval.append(perm)
   return retval

class Solution:

   def __init__(self):
       self.walks = []
   def get_min_walk_length(self):
       retval = 5000
       for walk in self.walks:
           if len(walk) < retval:
               retval = len(walk)
       return retval
   def has_similar_walk(self, test_walk):
       for walk in self.walks:
           if (walk[0] == test_walk[0] and
               walk[-1] == test_walk[-1]):
               return True
       return False
           

class Cell:

   def __init__(self, x, y, tp):
       self.value = get_pattern_value(x, y)
       if tp == 'c':
           self.value = (self.value + 1) % 2
       self.turn = get_turn_at(x, y, self.value)

class Board:

   def __init__(self, board_size):
       self.board_size = board_size
       self.rows = []
       self.cols = []
   def fill_in_good_board(self):
       for x in xrange(self.board_size):
           col = []
           for y in xrange(self.board_size):
               col.append(Cell(x, y, 'c'))
           self.cols.append(col)
       for y in xrange(self.board_size):
           row = []
           for x in xrange(self.board_size):
               row.append(Cell(x, y, 'r'))
           self.rows.append(row)
   def assign_names_based_on_solution(self, solution):
       names = ["A", "B", "C", "D", "E", "F"]
       self.names_map = {}
       name_index = 0
       for walk in solution.walks:
           if self.names_map.has_key(walk[0]):
               continue
           self.names_map[walk[0]] = names[name_index] 
           self.names_map[walk[-1]] = names[name_index] 
           name_index += 1 
   def generate_random_content(self, letters):
       for x in xrange(self.board_size):
           col = []
           for y in xrange(self.board_size):
               col.append(Cell())
           self.cols.append(col)
       for y in xrange(self.board_size):
           row = []
           for x in xrange(self.board_size):
               while 1:
                   col_cell = self.get_col_cell(x, y)
                   new_cell = Cell()
                   if new_cell.turn != col_cell.turn:
                       row.append(new_cell)
                       break
           self.rows.append(row)
       for i in xrange(len(letters)):
           x = i % self.board_size
           y = i / self.board_size
           value = 0
           if letters[i] != ' ':
               value = 1 + ord(letters[i]) - ord('a')
           other_value = random.randrange(value+1, 51)
           use_col = random.randrange(0, 2)
           col_cell = self.get_col_cell(x, y)
           row_cell = self.get_row_cell(x, y)
           if use_col:
               col_cell.value = value
               row_cell.value = other_value
           else:
               col_cell.value = other_value
               row_cell.value = value
   def get_col_cell(self, x, y):
       return self.cols[x][y]
   def get_row_cell(self, x, y):
       return self.rows[y][x]
   
   def get_turn(self, x, y):
       rcell = self.get_row_cell(x, y)
       ccell = self.get_col_cell(x, y)
       if rcell.value:
           return rcell.turn
       return ccell.turn
   def get_col_text_lines(self, x, y):
       ccell = self.get_col_cell(x, y)
       if ccell.value:
           return TrueFacts[x + y * 5]
       return FalseFacts[x + y * 5]
       
   def get_row_text_lines(self, x, y):
       rcell = self.get_row_cell(x, y)
       if rcell.value:
           return TrueFacts[x + y * 5]
       return FalseFacts[x + y * 5]
       
       
   def solve(self):
       solution = Solution()
       for i in xrange(self.board_size):
           start = (-1, i)
           direction = 'E'
           solution.walks.append(self.generate_walk([start], direction))
           
           start = (i, -1)
           direction = 'S'
           solution.walks.append(self.generate_walk([start], direction))
       return solution
   def generate_walk(self, current_walk, direction):
       previous_loc = current_walk[-1]
       
       new_location = self.move_in_direction(previous_loc, direction)
       if (new_location[0] == self.board_size or new_location[1] == self.board_size):
           (new_location, direction) = self.do_end_of_row_flip(new_location, direction)
       current_walk.append(new_location)
       if new_location[0] == -1 or new_location[1] == -1:
           return current_walk
       new_direction = self.do_board_turn(new_location, direction)
       return self.generate_walk(current_walk, new_direction)
   def move_in_direction(self, location, direction):
       if direction == 'E':
           return (location[0] + 1, location[1])
       if direction == 'W':
           return (location[0] - 1, location[1])
       if direction == 'S':
           return (location[0], location[1] + 1)
       if direction == 'N':
           return (location[0], location[1] - 1)
       assert(False)
   def do_end_of_row_flip(self, location, direction):
       # Should only happen moving south or east.
       assert(direction == 'S' or direction == 'E')
       new_location = location
       new_direction = None
           
       index = 0
       if direction == 'E':
           index = 1
       val = location[index]
       if (val & 0x1) == 0:
           new_val = val + 1
       else:
           new_val = val -1
       if (new_val == self.board_size):
           if direction == 'E':
               new_direction = 'N'
           else:
               new_direction = 'W'
           return ((self.board_size-1, self.board_size-1), new_direction)
       if index == 0:
           new_location = (new_val, new_location[1] - 1)
       else:
           new_location = (new_location[0] - 1, new_val)


       if direction == 'E':
           new_direction = 'W'
       else:
           new_direction = 'N'
       return (new_location, new_direction)
                   
   def do_board_turn(self, location, direction):
       turn = self.get_turn(location[0], location[1])
       if turn == '+':
           return direction
       if turn == '/':
           return FWD_TURNS[direction]
       return BACK_TURNS[direction] 
   def dump(self, dump_type):
       sys.stdout.write( "\n")
       sys.stdout.write( dump_type + ":\n")
       for y in xrange(self.board_size + 2):
           y = y - 1
           for x in xrange(self.board_size + 2):
               x = x - 1
               if x < 0:
                   if y < 0 or y == self.board_size:
                       sys.stdout.write("    ")
                   else:
                       sys.stdout.write(self.names_map[(x, y)] + "   ")
               elif y < 0:
                   if x == self.board_size:
                       sys.stdout.write("    ")
                   else:
                       sys.stdout.write(self.names_map[(x, y)] + "   ")
               elif x == self.board_size:
                   if (y & 0x1) == 0:
                       sys.stdout.write("\\" + "   ")
                   else:
                       sys.stdout.write("/" + "   ")
               elif y == self.board_size:
                   if (x & 0x1) == 0:
                       sys.stdout.write("\\" + "   ")
                   else:
                       sys.stdout.write("/" + "   ")
               else:
                   if dump_type == "row":
                       sys.stdout.write(self.get_row_cell(x, y).turn)
                       sys.stdout.write("%02d " %
                                        self.get_row_cell(x, y).value)
                   elif dump_type == "col":
                       sys.stdout.write(self.get_col_cell(x, y).turn)
                       sys.stdout.write("%02d " %
                                        self.get_col_cell(x, y).value)
                   else:
                       sys.stdout.write(self.get_turn(x, y))
                       sys.stdout.write("%02d " % 1)
           sys.stdout.write("\n")
   def permute_board(self, perm1, perm2):
       new_board = Board(self.board_size)
       for i in perm1:
           new_board.cols.append(self.cols[i])
       for i in perm2:
           new_board.rows.append(self.rows[i])
       return new_board
   def has_same_solution(self, solution):
       for i in xrange(self.board_size):
           start = (-1, i)
           direction = 'E'
           walk = self.generate_walk([start], direction)
           if not solution.has_similar_walk(walk):
               return False
           
           start = (i, -1)
           direction = 'S'
           if not solution.has_similar_walk(walk):
               return False
       return True
   def values_are_legit(self):
       for x in xrange(self.board_size):
           for y in xrange(self.board_size):
               rcell = self.get_row_cell(x, y)
               ccell = self.get_col_cell(x, y)
               if rcell.value == ccell.value:
                   return False
       return True


   def get_matching_solutions(self, solution):
       retval = []
       perms = make_perms(range(self.board_size))
       count = 0
       for perm1 in perms:
           for perm2 in perms:
               # Don't worry about where perm1 = perm2 = identity.
               if (perm1 == range(self.board_size) and
                   perm2 == range(self.board_size)):
                   continue
               new_board = self.permute_board(perm1, perm2)
               if not new_board.values_are_legit():
                   continue
               new_board.assign_names_based_on_solution(solution)
               count += 1
               print "%d legit boards." % count
               if new_board.has_same_solution(solution):
                   retval.append(new_board)
       return retval

def draw_cell(cell_type, x, y, im, color="white", lines=None):

   draw = ImageDraw.Draw(im)
   draw.rectangle((x, y, x + CELL_SIZE, y + CELL_SIZE), color)
   # Hash marks in corners.
   draw.rectangle((x, y, x + HASH_LENGTH, y + HASH_WIDTH), "grey")
   draw.rectangle((x, y, x + HASH_WIDTH, y + HASH_LENGTH), "grey")
   draw.rectangle((x + CELL_SIZE-HASH_LENGTH, y, x + CELL_SIZE, y + HASH_WIDTH), "grey")
   draw.rectangle((x + CELL_SIZE-HASH_WIDTH, y, x + CELL_SIZE, y + HASH_LENGTH), "grey")
   draw.rectangle((x, y + CELL_SIZE - HASH_WIDTH, x + HASH_LENGTH,
                   y + CELL_SIZE), "grey")
   draw.rectangle((x, y + CELL_SIZE - HASH_LENGTH, x + HASH_WIDTH,
                   y + CELL_SIZE), "grey")
   draw.rectangle((x + CELL_SIZE - HASH_LENGTH, y + CELL_SIZE - HASH_WIDTH,
                   x + CELL_SIZE, y + CELL_SIZE), "grey")
   draw.rectangle((x + CELL_SIZE - HASH_WIDTH, y + CELL_SIZE - HASH_LENGTH,
                   x + CELL_SIZE, y + CELL_SIZE), "grey")
   # Und now the marks!
   if (cell_type == '+'):
       draw.rectangle((x + (CELL_SIZE - LINE_WIDTH)/2, y,
                      x + (CELL_SIZE + LINE_WIDTH)/2, y + CELL_SIZE),
                      "grey")
       draw.rectangle((x, y + (CELL_SIZE - LINE_WIDTH)/2, 
                      x + (CELL_SIZE - 3 * LINE_WIDTH)/2,
                       y + (CELL_SIZE + LINE_WIDTH)/2),
                      "grey")
                   
       draw.rectangle((x + (CELL_SIZE + 3 * LINE_WIDTH)/2,
                       y + (CELL_SIZE - LINE_WIDTH)/2,
                      x + CELL_SIZE , y + (CELL_SIZE + LINE_WIDTH)/2),
                      "grey")
   elif (cell_type == ' '):
       return
   elif (cell_type == '/'):
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x - CELL_SIZE/2 - i, y - CELL_SIZE/2 - i,
                     x + CELL_SIZE/2 + i, y + CELL_SIZE/2 + i), 0, 90, "grey")
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x - CELL_SIZE/2 + i, y - CELL_SIZE/2 + i,
                     x + CELL_SIZE/2 - i, y + CELL_SIZE/2 - i), 0, 90, "grey")
           
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x + CELL_SIZE - CELL_SIZE/2 - i, y + CELL_SIZE - CELL_SIZE/2 - i,
                     x + CELL_SIZE + CELL_SIZE/2 + i, y + CELL_SIZE + CELL_SIZE/2 + i),
                    180, 270, "grey")
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x + CELL_SIZE - CELL_SIZE/2 + i, y + CELL_SIZE - CELL_SIZE/2 + i,
                     x + CELL_SIZE + CELL_SIZE/2 - i, y + CELL_SIZE + CELL_SIZE/2 - i),
                    180, 270, "grey")
   elif (cell_type == '\\'):
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x - CELL_SIZE/2 - i, y + CELL_SIZE- CELL_SIZE/2 - i,
                     x + CELL_SIZE/2 + i, y + CELL_SIZE + CELL_SIZE/2 + i),
                    270, 360, "grey")
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x - CELL_SIZE/2 + i, y + CELL_SIZE- CELL_SIZE/2 + i,
                     x + CELL_SIZE/2 - i, y + CELL_SIZE + CELL_SIZE/2 - i),
                    270, 360, "grey")
           
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x + CELL_SIZE - CELL_SIZE/2 - i, y - CELL_SIZE/2 - i,
                     x + CELL_SIZE + CELL_SIZE/2 + i, y + CELL_SIZE/2 + i),
                    90, 180, "grey")
       for i in xrange(LINE_WIDTH/2 + 1):
           draw.arc((x + CELL_SIZE - CELL_SIZE/2 + i, y - CELL_SIZE/2 + i,
                     x + CELL_SIZE + CELL_SIZE/2 - i, y + CELL_SIZE/2 - i),
                    90, 180, "grey")
   elif (cell_type == '|'):
       draw.rectangle((x + (CELL_SIZE - LINE_WIDTH)/2, y,
                      x + (CELL_SIZE + LINE_WIDTH)/2 , y + CELL_SIZE),
                      "grey")
   else:
       draw.rectangle((x, y + (CELL_SIZE - LINE_WIDTH)/2,
                      x + CELL_SIZE , y + (CELL_SIZE + LINE_WIDTH)/2),
                      "grey")
   # If text is not None, add it.
   l_count = 0
   if lines is not None:
       for line in lines:
           draw.text((x + 10, y + 10 + l_count * 20), line, "black")
           l_count += 1


def create_cols_image_file(board):

   c_count = 0
   image = Image.new("RGB", (5 * (CELL_SIZE + 5), CELL_SIZE * 9), "white")
   for col in board.cols:
       x = c_count * (CELL_SIZE + 5)
       r_count = 0
       for cell in col:
           y = r_count * CELL_SIZE * 2
           text_lines = board.get_col_text_lines(c_count, r_count)
           
           draw_cell(cell.turn, x, y, image, "white", text_lines)
           if r_count != len(col) - 1:
               draw_cell("|", x, y + CELL_SIZE, image, "white")
           r_count += 1
       c_count += 1
   image.show()
   image.save("cols.bmp")

def create_rows_image_file(board):

   image = Image.new("RGB", (CELL_SIZE * 9, 5 * (CELL_SIZE + 5)), "white")
   r_count = 0
   for row in board.rows:
       y = r_count * (CELL_SIZE + 5)
       c_count = 0
       for cell in row:
           x = c_count * CELL_SIZE * 2
           text_lines = board.get_row_text_lines(c_count, r_count)
           draw_cell(cell.turn, x, y, image, "white", text_lines)
          
           if c_count != len(row) - 1:
               draw_cell("-", x + CELL_SIZE, y, image, "white")
           c_count += 1
       r_count += 1
   image.show()
   image.save("rows.bmp")

def create_board_image_file(board):

   image = Image.new("RGB", (CELL_SIZE * 11,
                             CELL_SIZE * 11), "white")
   for i in xrange(11):
       for j in xrange(11):
           x = i * CELL_SIZE
           y = j * CELL_SIZE
           if i == 0:
               if j == 0:
                   draw_cell("/", x, y, image, "white")
               elif j == 10:
                   draw_cell(" ", x, y, image, "white")
               elif (j & 0x3) == 1:
                   draw_cell("\\", x, y, image, "white")
               elif (j & 0x3) == 3:
                   draw_cell("/", x, y, image, "white")
               else:
                   draw_cell("|", x, y, image, "white")
           elif j == 0:
               if i == 10:
                   draw_cell(" ", x, y, image, "white")
               elif (i & 0x3) == 1:
                   draw_cell("\\", x, y, image, "white")
               elif (i & 0x3) == 3:
                   draw_cell("/", x, y, image, "white")
               else:
                   draw_cell("-", x, y, image, "white")
           elif i == 10 or j == 10:
               draw_cell(" ", x, y, image, "white")                    
           else:
               color = "white"
               draw_cell(" ", x, y, image, color)
   image.show()
   image.save("board.png")
   
                   

count = 0 while 1:

   # Make a random 5x5 board.
   board = Board(5)
   board.fill_in_good_board()
   
   assert(board.values_are_legit())
   solution = board.solve()
   # If the smallest walk from A to B is too short, bail, make a new board.
   if solution.get_min_walk_length() < 8:
       continue
   # Dump this board.
   print "A board with non-trivial solution:"
   board.assign_names_based_on_solution(solution)
   
   # Are there any other permutations of rows that give the same pairs?
   matching_solutions = board.get_matching_solutions(solution)
   if len(matching_solutions) > 0:
       print "  Has " + str(len(matching_solutions)) + " matching solutions."
       print "  An example: "
       matching_solutions[0].dump("row")
       matching_solutions[0].dump("col")
       matching_solutions[0].dump("all")
       count += 1
       if count >= 200:
           print "I quit!"
           break
   else:
       print "Winner!"


       board.dump("row")
       board.dump("col")
       board.dump("all")
       create_rows_image_file(board)
       create_cols_image_file(board)
       create_board_image_file(board)
       break