Мартин обнови решението на 14.04.2013 13:39 (преди над 11 години)
+import re
+from collections import OrderedDict
+from itertools import chain
+
+
+class InvalidValue(Exception):
+ pass
+
+
+class InvalidKey(Exception):
+ pass
+
+
+class InvalidMove(Exception):
+ pass
+
+
+class NotYourTurn(Exception):
+ pass
+
+
+def validates_key(fn):
+ def wrapped(key):
+ if not isinstance(key, str) or not re.match('[A-C][1-3]', key):
+ raise InvalidKey()
+ return fn(key)
+ return wrapped
+
+
+class TicTacToeBoard:
+
+ VALID_VALUES = ('X', 'O')
+ WINNING_COMBINATIONS = (('X', 'X', 'X'),
+ ('O', 'O', 'O'))
+ NUMBER_OF_SQUARES = 9
+
+ def __init__(self):
+ self._dictBoard = OrderedDict.fromkeys(('A', 'B', 'C'))
+ for char in self._dictBoard:
+ self._dictBoard[char] = [' ', ' ', ' ']
+ self._is_finished = False
+ self._winner = None
+ self._last_move = None
+ self._moves_counter = 0
+
+ def __getitem__(self, key):
+ char, digit = self.__class__.cleaned_key(key)
+ return self._dictBoard[char][digit]
+
+ def __setitem__(self, key, value):
+ self._validate_move(key, value)
+ char, digit = self.__class__.cleaned_key(key)
+ self._dictBoard[char][digit] = value
+ self._last_move = value
+ self._moves_counter += 1
+ if not self._is_finished:
+ self._resolve_status()
+
+ def game_status(self):
+ if self._is_finished:
+ if self._winner:
+ return "{} wins!".format(self._winner)
+ else:
+ return "Draw!"
+ return 'Game in progress.'
+
+ def __str__(self):
+ return ('\n -------------\n' +
+ '3 | {0[2]} | {1[2]} | {2[2]} |\n' +
+ ' -------------\n' +
+ '2 | {0[1]} | {1[1]} | {2[1]} |\n' +
+ ' -------------\n' +
+ '1 | {0[0]} | {1[0]} | {2[0]} |\n' +
+ ' -------------\n' +
+ ' A B C \n').format(*self._columns)
+
+ @staticmethod
+ @validates_key
+ def cleaned_key(key):
+ char, digit = list(key)
+ digit = int(digit) - 1
+ return (char, digit)
+
+ @property
+ def _rows(self):
+ return tuple(zip(*self._columns))
+
+ @property
+ def _columns(self):
+ return tuple(tuple(self._dictBoard[char]) for char in self._dictBoard)
+
+ @property
+ def _diagonals(self):
+ return ([col[i] for i, col in enumerate(self._columns)],
+ [col[i] for i, col in enumerate(reversed(self._columns))])
+
+ def _resolve_status(self):
+ for triple in chain(self._columns, self._rows, self._diagonals):
+ if triple in self.WINNING_COMBINATIONS:
+ self._is_finished = True
+ self._winner = triple[0]
+ return
+ if self._moves_counter == self.NUMBER_OF_SQUARES:
+ self._is_finished = True
+
+ def _validate_move(self, key, value):
+ char, digit = self.__class__.cleaned_key(key)
+ if not value in self.VALID_VALUES:
+ raise InvalidValue()
+ if self._dictBoard[char][digit] != ' ':
+ raise InvalidMove()
+ if self._last_move == value:
+ raise NotYourTurn()