Created
July 23, 2012 14:46
-
-
Save stephenroller/3163995 to your computer and use it in GitHub Desktop.
2 Player Dots and Boxes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import curses, sys, re | |
from time import sleep | |
from random import randint | |
OFFSET_X, OFFSET_Y = 2, 1 | |
BOARD_SIZE = 7 | |
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | |
# init curses | |
stdscr = curses.initscr() | |
curses.noecho() | |
curses.start_color() | |
stdscr.keypad(1) | |
curses.init_pair(1, curses.COLOR_YELLOW, curses.COLOR_BLACK) | |
curses.init_pair(2, curses.COLOR_BLUE, curses.COLOR_BLACK) | |
curses.init_pair(3, curses.COLOR_BLACK, curses.COLOR_RED) | |
def init_board(): | |
# The structure of the board is a bit confusing, but it looks like this | |
# The even indexed rows have fewer values because there are fewer possible | |
# lines on them. | |
# o---o o [ [ True, False ], | |
# | [ True, False, False], | |
# o o---o [ False, True ], | |
# | [ False, True, False], | |
# o---o---o [ True, True] ] | |
even = [False for i in xrange(BOARD_SIZE - 1)] | |
odd = [False for i in xrange(BOARD_SIZE)] | |
board = [] | |
for i in xrange(BOARD_SIZE * 2 - 1): | |
if i % 2 == 0: | |
board.append(even[:]) | |
else: | |
board.append(odd[:]) | |
return board | |
def init_score(): | |
# The scorecard is a little bit redundant, but makes things easier | |
# Its size is (BOARD_SIZE-1)^2 because we have to store more lines than | |
# dots. | |
# | |
# o - o o [ | |
# | 1 | [ 1, None ], | |
# o - o - o | |
# | 2 | [ None, 2 ] | |
# o o - o ] | |
# | |
return [[None for a in xrange(BOARD_SIZE-1)] for b in xrange(BOARD_SIZE-1)] | |
def closest_free(board, x, y): | |
def distance(x1, y1, x2, y2): | |
from math import sqrt | |
return sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2) | |
distances = [] | |
for i,row in enumerate(board): | |
for j,val in enumerate(row): | |
if val == False: | |
distances.append((distance(x, y, i, j), i, j)) | |
if len(distances) == 0: | |
return False | |
distances.sort(key=lambda x: x[0]) | |
return distances[0][1:] | |
def first_available_move(board): | |
try: | |
return closest_free(board, 0, 0) | |
except IndexError: | |
return False | |
def draw_dot(x, y): | |
sx = OFFSET_X + x * 4 | |
sy = OFFSET_Y + y | |
stdscr.addstr(sy, sx, "o") | |
stdscr.addstr(sy, sx + 4, "o") | |
def draw_line(x, y, color=0): | |
# print "%d, %d" % (x, y) | |
if y % 2 == 0: | |
sy = OFFSET_Y + y | |
sx = OFFSET_X + x * 4 + 1 | |
stdscr.addstr(sy, sx, '---', curses.color_pair(color)) | |
else: | |
sy = OFFSET_Y + y | |
sx = OFFSET_X + x * 4 | |
stdscr.addstr(sy, sx, "|", curses.color_pair(color)) | |
def draw_filling(x, y, player): | |
screen_x = OFFSET_X + x * 4 + 2 | |
screen_y = OFFSET_Y + y * 2 + 1 | |
stdscr.addstr(screen_y, screen_x, str(player), curses.color_pair(player)) | |
def play_game(): | |
board = init_board() | |
score = init_score() | |
draw_board(board, score) | |
current_player = 1 # alternates between 1 and 2 | |
def check_and_set_square(x, y): | |
if score[x][y]: | |
return 0 | |
if (board[x * 2][y] and # above | |
board[x * 2 + 1][y] and # left | |
board[x * 2 + 1][y + 1] and # right | |
board[x * 2 + 2][y]): # bottom | |
score[x][y] = current_player | |
return 1 | |
return 0 | |
selected_x, selected_y = 0, 0 | |
while first_available_move(board): | |
y = OFFSET_Y + BOARD_SIZE * 2 + 1 | |
# Let's handle the selector: | |
selected_x, selected_y = closest_free(board, selected_x, selected_y) | |
# Read from the keyboard | |
c = None | |
while c != ord("\n") or board[selected_x][selected_y]: | |
stdscr.clear() | |
draw_board(board, score) | |
color = board[selected_x][selected_y] and 3 or current_player | |
draw_line(selected_y, selected_x, color) | |
c = stdscr.getch() | |
if c == curses.KEY_UP: | |
size = selected_x % 2 == 0 and 2 * BOARD_SIZE - 1 or 2 * BOARD_SIZE | |
selected_x = (selected_x - 1) % size | |
if selected_y >= len(board[selected_x]): | |
selected_y = len(board[selected_x]) - 1 | |
elif selected_y == len(board[selected_x]) - 2 and selected_x % 2 == 1: | |
selected_y += 1 | |
elif c == curses.KEY_DOWN: | |
size = selected_x % 2 == 0 and 2 * BOARD_SIZE - 1 or 2 * BOARD_SIZE | |
selected_x = (selected_x + 1) % size | |
if selected_y >= len(board[selected_x]): | |
selected_y = len(board[selected_x]) - 1 | |
elif selected_y == len(board[selected_x]) - 2 and selected_x % 2 == 1: | |
selected_y += 1 | |
elif c == curses.KEY_LEFT: | |
size = selected_x % 2 == 0 and BOARD_SIZE - 1 or BOARD_SIZE | |
selected_y = (selected_y - 1) % size | |
elif c == curses.KEY_RIGHT: | |
size = selected_x % 2 == 0 and BOARD_SIZE - 1 or BOARD_SIZE | |
selected_y = (selected_y + 1) % size | |
x1,y1 = selected_x,selected_y | |
filled_boxes = 0 | |
board[selected_x][selected_y] = True | |
if selected_x % 2 == 0: | |
# Now it's time to check for new boxes | |
# o o | |
# | |
# o---o A new horizontal line can only fill in above | |
# or below | |
# o o | |
if x1 > 0: | |
# check above | |
filled_boxes += check_and_set_square((x1-1)/2, y1) | |
if (x1-1)/2+1 < BOARD_SIZE-1: | |
# check below | |
filled_boxes += check_and_set_square((x1-1)/2+1, y1) | |
else: | |
# Now it's time to check for new boxes | |
# o o o A new vertical line can only fill in | |
# | on the left or right | |
# o o o | |
if y1 > 0: | |
# check left | |
filled_boxes += check_and_set_square((x1-1)/2, y1-1) | |
if y1 < BOARD_SIZE - 1: | |
# check right | |
filled_boxes += check_and_set_square((x1-1)/2, y1) | |
if filled_boxes == 0: | |
# switch players | |
current_player = current_player % 2 + 1 | |
draw_board(board, score) | |
stdscr.refresh() | |
def draw_board(board, scores): | |
# +-------> y-axis | |
# | 1 2 | |
# | A . . | |
# | B . . | |
# v | |
# x-axis | |
# | |
# draw the headers | |
# now draw the board | |
for i, row in enumerate(board): | |
for j, column in enumerate(row): | |
# print "here %d, %d" % (i, j) | |
if i % 2 == 0: | |
draw_dot(j, i) | |
if column: | |
draw_line(j, i) | |
stdscr.refresh() | |
# We need to draw the letters in the boxes too and add the score | |
player1, player2 = 0, 0 | |
for i, row in enumerate(scores): | |
for j, score in enumerate(row): | |
if score == 1: player1 += 1 | |
if score == 2: player2 += 1 | |
if score: | |
draw_filling(j, i, score) | |
# Draw the score board | |
screen_y = OFFSET_Y + BOARD_SIZE * 2 | |
screen_x = OFFSET_X | |
stdscr.addstr(screen_y, screen_x, "Player 1: %d" % player1, | |
curses.color_pair(1)) | |
stdscr.addstr(screen_y + 1, screen_x, "Player 2: %d" % player2, | |
curses.color_pair(2)) | |
stdscr.refresh() | |
if __name__ == '__main__': | |
try: | |
play_game() | |
except KeyboardInterrupt: | |
pass | |
finally: | |
curses.endwin() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment