# 0h h1 Solver. Solves grids of 0h h1 game. # Copyright (C) 2015 Gabriel Augendre # # This program 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. # # This program 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 this program. If not, see . __author__ = 'gaugendre' import sys class Grid: def __init__(self, size, array=None): _squares = [] square_list = [] i = 0 while i < size: _squares.append([]) j = 0 while j < size: if array: _squares[i].append(Square(self, i, j, array[i][j])) else: _squares[i].append(Square(self, i, j)) j += 1 square_list.extend(_squares[i]) i += 1 self._squares = _squares self.size = size self.square_list = square_list def _get_squares(self): return self._squares squares = property(fget=_get_squares) def square(self, horiz, vert): return self._squares[vert][horiz] def __repr__(self): representation = "" for line in self._squares: for square in line: to_print = square.state if to_print == ' ': to_print = '_' representation += to_print + ' ' representation += "\n" return representation def solve_three(self): for square in self.square_list: if square.is_empty(): v_prev = square.prev_vert() v_next = square.next_vert() h_prev = square.prev_horiz() h_next = square.next_horiz() if not square.switched and v_prev and v_prev.state != ' ': v_p_prev = v_prev.prev_vert() if v_p_prev and v_p_prev.state == v_prev.state: square.state = v_prev.opposite_state() elif v_next and v_next.state == v_prev.state: square.state = v_prev.opposite_state() if not square.switched and v_next and v_next.state != ' ': v_n_next = v_next.next_vert() if v_n_next and v_n_next.state == v_next.state: square.state = v_next.opposite_state() if not square.switched and h_prev and h_prev.state != ' ': h_p_prev = h_prev.prev_horiz() if h_p_prev and h_p_prev.state == h_prev.state: square.state = h_prev.opposite_state() elif h_next and h_next.state == h_prev.state: square.state = h_prev.opposite_state() if not square.switched and h_next and h_next.state != ' ': h_n_next = h_next.next_horiz() if h_n_next and h_n_next.state == h_next.state: square.state = h_next.opposite_state() def solve(self): for i in range(0, 2, 1): self.solve_three() self.solve_same_number() def solve_same_number(self): for square in self.square_list: if square.is_empty(): same_line = square.same_line() count_red = 0 count_blue = 0 for line_square in same_line: if line_square.state == 'B': count_blue += 1 elif line_square.state == 'R': count_red += 1 if count_red == self.size / 2: square.state = 'B' elif count_blue == self.size / 2: square.state = 'R' if not square.switched: same_column = square.same_column() count_red = 0 count_blue = 0 for line_square in same_column: if line_square.state == 'B': count_blue += 1 elif line_square.state == 'R': count_red += 1 if count_red == self.size / 2: square.state = 'B' elif count_blue == self.size / 2: square.state = 'R' class Square: """ Represents a square in the grid. A square can be either Red, Blue, or Nothing, depending on the text written in it and displayed ('R', 'B' or ' '). """ def __init__(self, grid, vert, horiz, state=' '): self.horiz = horiz self.vert = vert self.switched = False if isinstance(grid, Grid): self.grid = grid else: print("Warning : Attribute grid not instance of Grid", file=sys.stderr) self.grid = None if state in (' ', 'R', 'B'): self._state = state else: print("Warning : Attribute state not in ('R', 'B', ' ')", file=sys.stderr) self._state = ' ' def next_horiz(self): if self.horiz == self.grid.size - 1: return None return self.grid.square(self.horiz + 1, self.vert) def prev_horiz(self): if self.horiz == 0: return None return self.grid.square(self.horiz - 1, self.vert) def next_vert(self): if self.vert == self.grid.size - 1: return None return self.grid.square(self.horiz, self.vert + 1) def prev_vert(self): if self.vert == 0: return None return self.grid.square(self.horiz, self.vert - 1) def __eq__(self, other): if other is None or not isinstance(other, Square): return False else: return self.__hash__() == other.__hash__() def __hash__(self): return hash((self.horiz, self.vert, self.grid)) def __repr__(self): return "({}, {}) : '{}'".format(self.horiz, self.vert, self.state) def _get_state(self): """ Allow to get square state. :return: The square state. Either ' ', 'R' or 'B' """ return self._state def _set_state(self, new_state): """ Changes square state. Accepts only 'R', 'B', or ' '. Other values are not accepted and the square is not modified. """ if new_state in ('R', 'B', ' '): self._state = new_state self.switched = True else: print("Error :", new_state, "not in ('R', 'B', ' ').") state = property(_get_state, _set_state) def opposite_state(self): if self.state == 'R': return 'B' elif self.state == 'B': return 'R' else: return ' ' def is_empty(self): return self.state == ' ' def all_prev_horiz(self): h_prev = self.prev_horiz() all_prev_horiz_list = [] while h_prev: all_prev_horiz_list.append(h_prev) h_prev = h_prev.prev_horiz() return all_prev_horiz_list def all_next_horiz(self): h_next = self.next_horiz() all_next_horiz_list = [] while h_next: all_next_horiz_list.append(h_next) h_next = h_next.next_horiz() return all_next_horiz_list def all_prev_vert(self): v_prev = self.prev_vert() all_prev_vert_list = [] while v_prev: all_prev_vert_list.append(v_prev) v_prev = v_prev.prev_vert() return all_prev_vert_list def all_next_vert(self): v_next = self.next_vert() all_next_vert_list = [] while v_next: all_next_vert_list.append(v_next) v_next = v_next.next_vert() return all_next_vert_list def same_line(self): """ List of squares in the same line. Does not include the considered square. :return: The list of the squares in the same line. """ line_list = [] line_list.extend(self.all_prev_horiz()) line_list.extend(self.all_next_horiz()) return line_list def same_column(self): """ List of squares in the same column. Does not include the considered square. :return: The list of the squares in the same column. """ line_list = [] line_list.extend(self.all_prev_vert()) line_list.extend(self.all_next_vert()) return line_list