Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- #
- # Copyright (C) 2011 by Yu-Jie Lin
- #
- # Permission is hereby granted, free of charge, to any person obtaining a copy
- # of this software and associated documentation files (the "Software"), to deal
- # in the Software without restriction, including without limitation the rights
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- # copies of the Software, and to permit persons to whom the Software is
- # furnished to do so, subject to the following conditions:
- #
- # The above copyright notice and this permission notice shall be included in
- # all copies or substantial portions of the Software.
- #
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- # THE SOFTWARE.
- import sys
- import urwid
- class Board(object):
- players = ('X', 'O')
- class MarkFont1(urwid.Font):
- height = 5
- data = [u"""
- OOOOOOOOOOXXXXXXXXXX
- ****** ** **
- ** ** ** **
- ** ** **
- ** ** ** **
- ****** ** **
- """]
- class MarkFont2(urwid.Font):
- height = 5
- data = [u"""
- OOOOOOOOOOXXXXXXXXXX
- ▄██████▄ ██ ██
- ██ ██ ██ ██
- ██ ██ ██
- ██ ██ ██ ██
- ▀██████▀ ██ ██
- """]
- class Cell(urwid.BigText):
- _selectable = True
- signals = ['place']
- def keypress(self, size, key):
- if key == ' ' and self.get_text()[0] == ' ':
- self._emit('place')
- else:
- return key
- def mouse_event(self, size, event, button, col, row, focus):
- if event == 'mouse press' and button == 1 and self.get_text()[0] == ' ':
- self._emit('place')
- return True
- def __init__(self, size=None, winning_pieces=3, font=None):
- if isinstance(size, int):
- size = (size, size)
- elif size is None or not isinstance(size, tuple) or len(size) != 2:
- size = (3, 3)
- self.size = size
- self.winning_pieces = winning_pieces
- self.font = font if font is not None else Board.MarkFont2()
- self.reset()
- def reset(self):
- self.ended = False
- w, h = self.size
- self.marks = ['']*(w*h)
- self.player = 0
- if self.winning_pieces > max(w, h):
- self.winning_pieces = max(w, h)
- cells = []
- for i in range(w * h):
- cell = Board.Cell(' ', self.font)
- urwid.connect_signal(cell, 'place', self.place)
- cell = urwid.Padding(cell, align='center', width='clip')
- cell = urwid.AttrMap(cell, 'normal', 'select')
- cells.append(cell)
- self.cells = cells
- char_width = max(self.font.char_width(char) for char in Board.players)
- self._grid = urwid.GridFlow(cells, cell_width=char_width, h_sep=2, v_sep=1, align='center')
- self._grid.set_focus(w / 2 + (h / 2) * w)
- self.grid = urwid.Padding(self._grid, align='center', width=w*char_width + self._grid.h_sep*(w-1))
- def place(self, w):
- if self.ended:
- return
- for cell in self.cells:
- if cell.base_widget is w:
- break
- index = self.cells.index(cell)
- if self.marks[index]:
- return
- mark = Board.players[self.player]
- self.marks[index] = mark
- cell.base_widget.set_text(mark)
- if self.check_win(index):
- return
- self.player += 1
- if self.player >= len(Board.players):
- self.player = 0
- urwid.emit_signal(self, 'player_change')
- def i_xy(self, index):
- return index % self.size[0], index / self.size[0]
- def xy_i(self, x, y):
- return y * self.size[0] + x
- def count_pieces(self, x, y, inc_x, inc_y, mark=False):
- count = 0
- p = self.marks[self.xy_i(x, y)]
- while self.marks[self.xy_i(x, y)] == p:
- if mark:
- self.cells[self.xy_i(x, y)].set_attr_map({None: 'win'})
- self.cells[self.xy_i(x, y)].set_focus_map({None: 'select win'})
- x += inc_x
- y += inc_y
- count += 1
- if x >= self.size[0] or y >= self.size[1]:
- break
- return count
- def _check_win(self, ix, iy, chk_X, chk_Y):
- ip = self.marks[self.xy_i(ix, iy)]
- x, y = ix, iy
- while (not chk_X or x >= 0) and (not chk_Y or (y >= 0 and y < self.size[1])):
- if self.marks[self.xy_i(x, y)] != ip:
- x += 1 if chk_X else 0
- y += 1 if chk_Y is True else (0 if chk_Y is not -1 else -1)
- break
- if (not chk_X or x == 0) or (chk_Y is True and y == 0) or (chk_Y is -1 and y == self.size[1] - 1):
- break
- x -= 1 if chk_X else 0
- y -= 1 if chk_Y is True else (0 if chk_Y is not -1 else -1)
- if self.count_pieces(x, y, 1 if chk_X else 0, 1 if chk_Y is True else (0 if chk_Y is not -1 else -1)) >= self.winning_pieces:
- self.ended = True
- self.count_pieces(x, y, 1 if chk_X else 0, 1 if chk_Y is True else (0 if chk_Y is not -1 else -1), True)
- return True
- def check_win(self, index):
- ip = self.marks[index]
- ix, iy = self.i_xy(index)
- if self._check_win(ix, iy, True, False) or \
- self._check_win(ix, iy, False, True) or \
- self._check_win(ix, iy, True, True) or \
- self._check_win(ix, iy, True, -1):
- urwid.emit_signal(self, 'game_ended')
- return True
- if len(filter(None, self.marks)) == len(self.marks):
- self.ended = -1
- urwid.emit_signal(self, 'game_ended')
- return True
- urwid.register_signal(Board, ['player_change', 'game_ended'])
- class Game(object):
- palette = [
- ('normal', 'light blue', 'dark gray'),
- ('select', 'light blue', 'dark green'),
- ('win', 'light red', 'dark gray'),
- ('select win', 'light red', 'dark green'),
- ('status bar', 'white', 'dark blue'),
- ('status bar player', 'light red', 'dark blue'),
- ('keyhint bar', 'white', 'dark blue'),
- ('key', 'light blue', 'white'),
- ]
- def update_footer(self):
- def _get_keyhint_text(keys):
- return [
- _item
- for _tuple in ((('key', ' %s ' % key), ' %s ' % desc) for key, desc in keys)
- for _item in _tuple
- ]
- if self.board.ended:
- if self.board.ended == -1:
- self.status.set_text("Draw!")
- else:
- self.status.set_text("Player %s won!" % self.board.players[self.board.player])
- self.keyhint.set_text(_get_keyhint_text((
- ('Arrow keys', 'Move cursor'),
- ('w/W', 'Width'),
- ('h/H', 'Height'),
- ('-/+', 'Pieces'),
- ('R/Right Button', 'Restart'),
- ('Q', 'Quit'),
- ))
- )
- else:
- self.status.set_text([
- '%d pieces to win. ' % self.board.winning_pieces,
- ('status bar player', '%s' % self.board.players[self.board.player]),
- " player's turn..."
- ])
- self.keyhint.set_text(_get_keyhint_text((
- ('Arrow keys', 'Move cursor'),
- ('Space/Left Button', 'Place'),
- ('w/W', 'Width'),
- ('h/H', 'Height'),
- ('-/+', 'Pieces'),
- ('R/Right Button', 'Restart'),
- ('Q', 'Quit'),
- ))
- )
- def unhandled_input(self, key):
- if key in ('q', 'Q', 'esc'):
- raise urwid.ExitMainLoop
- if key in ('r', 'R') or (urwid.is_mouse_event(key) and key[1] == 3):
- pass
- elif key in ('w', 'W', 'h', 'H'):
- w, h = self.board.size
- if key in ('w', 'W'):
- w += -1 if key == 'w' else 1
- else:
- h += -1 if key == 'h' else 1
- w = max(1, w)
- h = max(1, h)
- self.board.size = (w, h)
- elif key in ('-', '+'):
- self.board.winning_pieces += -1 if key == '-' else 1
- self.board.winning_pieces = max(1, self.board.winning_pieces)
- else:
- return
- self.board.reset()
- self.board_filler.set_body(self.board.grid)
- self.update_footer()
- def run(self):
- self.board = Board(size=(3, 3))
- urwid.connect_signal(self.board, 'player_change', self.update_footer)
- urwid.connect_signal(self.board, 'game_ended', self.update_footer)
- self.board_filler = urwid.Filler(self.board.grid)
- self.keyhint = urwid.Text('')
- self.status = urwid.Text('')
- self.footer = urwid.Pile([urwid.AttrMap(self.status, 'status bar'),
- urwid.AttrMap(self.keyhint, 'keyhint bar')])
- self.update_footer()
- self.loop = urwid.MainLoop(urwid.Frame(self.board_filler,
- footer=self.footer), Game.palette, unhandled_input=self.unhandled_input)
- self.loop.run()
- def main():
- try:
- Game().run()
- except KeyboardInterrupt:
- pass
- if __name__ == '__main__':
- main()
Add Comment
Please, Sign In to add comment