Added the boardgame tic-tac-toe without winning conditions, written entirely in Python.
This commit is contained in:
parent
0590fa6ca1
commit
affcb739b4
28 changed files with 448 additions and 0 deletions
BIN
games/tic-tac-toe/.main.py.swp
Normal file
BIN
games/tic-tac-toe/.main.py.swp
Normal file
Binary file not shown.
91
games/tic-tac-toe/TODO
Normal file
91
games/tic-tac-toe/TODO
Normal file
|
@ -0,0 +1,91 @@
|
|||
- background image
|
||||
- subimages for game_area:s
|
||||
- subimages for game markers (X or 0)
|
||||
- rectangle collision on game_area:s
|
||||
- redraw background then all game_area:s
|
||||
- board_state: a hashtable where key is game_area_index
|
||||
and value is either'x' 'o' or ' '
|
||||
|
||||
board contains nr_of_squares = 9
|
||||
board contains array of squares with nr_of_squares elements
|
||||
board contains x, y, width and height
|
||||
board contains a frame
|
||||
|
||||
frame contains width and height
|
||||
|
||||
game_area contains.
|
||||
game_area contains index.
|
||||
|
||||
|
||||
|
||||
game init:
|
||||
t
|
||||
//frame is initialized with given width and height
|
||||
//frame is initialized with color
|
||||
//board is initialized with frame
|
||||
board is initialized with given nr_of_squares
|
||||
square_dimensions is initialized with x, y width and height
|
||||
board is initialized with square_dimensions
|
||||
board method init() is invoked
|
||||
|
||||
|
||||
|
||||
|
||||
board:init(canva, nr_of_squares, square_dimensions):
|
||||
img_sqr_x = load x image from harddisk
|
||||
img_sqr_y = load y image from harddisk
|
||||
|
||||
squares = new array of squares with nr_of squares elements
|
||||
|
||||
for i in nr_of_squares:
|
||||
square[i].init(square_dimensions, i, nr_of_squares)
|
||||
|
||||
|
||||
state of all game_areas is set to ' '.
|
||||
|
||||
board:paint():
|
||||
frame_paint()
|
||||
squares_paint()
|
||||
|
||||
|
||||
board:
|
||||
ptr canva
|
||||
img_sqr_x
|
||||
img_sqr_o
|
||||
//img_sqr_blank
|
||||
|
||||
//frame
|
||||
|
||||
array of squares
|
||||
|
||||
squares_paint():
|
||||
for square in squares:
|
||||
// May paint blank also
|
||||
if square.state is x:
|
||||
canva.paint(square.x, square.y, img_sqr_x)
|
||||
elif square.state is o:
|
||||
canva.paint(square.x, square.y, img_sqr_o)
|
||||
|
||||
square:
|
||||
int x,y,width,height
|
||||
char state
|
||||
|
||||
init(square_dimensions, index, nr_of_squares):
|
||||
//calculate x and y based on width, height and index
|
||||
//should really been done in squares class instead
|
||||
self.x = width * (index % (sqrt(nr_of_squares)))
|
||||
self.y = height * (index % (sqrt(nr_of_squares)))
|
||||
state is ' '
|
||||
|
||||
|
||||
|
||||
|
||||
all values of board_state is ' '.
|
||||
array of game_area:s initialized with game_positions
|
||||
according to the board
|
||||
-game loop:
|
||||
1. print background
|
||||
2. f
|
||||
|
||||
--marked gamepositions when hovering them
|
||||
|
BIN
games/tic-tac-toe/board.png
Normal file
BIN
games/tic-tac-toe/board.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 264 KiB |
18
games/tic-tac-toe/board.py
Normal file
18
games/tic-tac-toe/board.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
from rectangle import Rectangle
|
||||
from gamerectangle import GameRectangle
|
||||
from math import sqrt
|
||||
#param: nr_of_squares, dimensions(could be a Rectangle)
|
||||
#creates an array of gamerectangles within itself with correct
|
||||
#positions from each other
|
||||
|
||||
class Board(object):
|
||||
def __init__(self, nr_of_rectangles, dimensions):
|
||||
self.game_rectangles = []
|
||||
axis = sqrt(nr_of_rectangles)
|
||||
width = dimensions.width
|
||||
height = dimensions.height
|
||||
for index in range(nr_of_rectangles):
|
||||
x = width * (index % axis)
|
||||
y = height * int(index / axis)
|
||||
gr = GameRectangle(index, x, y, width, height)
|
||||
self.game_rectangles.append(gr)
|
BIN
games/tic-tac-toe/board.pyc
Normal file
BIN
games/tic-tac-toe/board.pyc
Normal file
Binary file not shown.
BIN
games/tic-tac-toe/e.png
Normal file
BIN
games/tic-tac-toe/e.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.5 KiB |
7
games/tic-tac-toe/gamerectangle.py
Normal file
7
games/tic-tac-toe/gamerectangle.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from rectangle import Rectangle
|
||||
|
||||
class GameRectangle(Rectangle):
|
||||
def __init__(self, index, x, y, width, height):
|
||||
self.index = index
|
||||
self.state = ' '
|
||||
Rectangle.__init__(self, x, y, width, height)
|
BIN
games/tic-tac-toe/gamerectangle.pyc
Normal file
BIN
games/tic-tac-toe/gamerectangle.pyc
Normal file
Binary file not shown.
23
games/tic-tac-toe/input.py
Normal file
23
games/tic-tac-toe/input.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
#Input.py
|
||||
|
||||
#Variables: Key_Id[Int,String], KeyBehaviour[enum], Action[method]
|
||||
#Methods: update() Param nothing,
|
||||
#Behaviour
|
||||
|
||||
|
||||
#in update. when called. takes the state of the key. compares with the behaviour.
|
||||
#if fullfilled perform action.
|
||||
|
||||
|
||||
#All Input instances have an id.
|
||||
class Input(object):
|
||||
id = -1
|
||||
fun = None
|
||||
def __init__(self, id, fun):
|
||||
self.id = id
|
||||
self.fun = fun
|
||||
|
||||
def notify(self):
|
||||
pass
|
||||
def update(self,state):
|
||||
pass
|
BIN
games/tic-tac-toe/input.pyc
Normal file
BIN
games/tic-tac-toe/input.pyc
Normal file
Binary file not shown.
71
games/tic-tac-toe/inputmanager.py
Normal file
71
games/tic-tac-toe/inputmanager.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
#Input_Manager.py
|
||||
#
|
||||
|
||||
from sys import exit
|
||||
import pygame.event as event
|
||||
import pygame.key as key
|
||||
from pygame import QUIT,KEYDOWN,KEYUP,MOUSEBUTTONDOWN,MOUSEBUTTONUP
|
||||
from keypress import KeyPress
|
||||
from mousepress import MousePress
|
||||
|
||||
|
||||
#inputlist: [Input]
|
||||
#Input = {Key_Id, Key_Behaviour, Action}
|
||||
#Key_Id: Id of a pygame key or a String matching the pygame key
|
||||
#Key_Behaviour: OnPress OnRelease etc
|
||||
#Action: A function to perform when the given input occurs.
|
||||
|
||||
#Key_Dictionary: A dictionary of Strings for the corresponding pygame key id:s
|
||||
#To speed up input lookups a store a dictionary where each key corresponds to an input or a set of inputs
|
||||
#for each update, lookup each key input in dict to get the affected inputs only
|
||||
|
||||
#Each Input subclass name must begin with Key
|
||||
|
||||
|
||||
#Functions
|
||||
#add: Add an input to the list of inputs
|
||||
#remove: Remove an input from the list of inputs
|
||||
#Process: For all inputs, check if any of them is occuring and trigger the corresponding action
|
||||
|
||||
|
||||
#Defect: Press two keys at the same time and release them. Should some time give 2 KeyUp events but sometimes only gets one.
|
||||
|
||||
class InputManager(object):
|
||||
#param: [Input]
|
||||
#requires:Pygame is initialized
|
||||
inputs = None
|
||||
def __init__(self, inputs):
|
||||
self.inputs = []
|
||||
for i in inputs: #LAST UPDATED HERE!!!
|
||||
if i[0] is "Key":
|
||||
key_behaviour = "Key" + i[2]
|
||||
#key_behaviour = key_behaviour + "." + key_behaviour
|
||||
self.inputs.append(eval(key_behaviour)(i[1],i[3]))
|
||||
elif i[0] is "Mouse":
|
||||
mouse_behaviour = "Mouse" + i[2]
|
||||
#mouse_behaviour = mouse_behaviour + "." + mouse_behaviour
|
||||
self.inputs.append(eval(mouse_behaviour)(i[1],i[3]))
|
||||
#desc: Look up the refreshed current input state and call the affected methods
|
||||
def update(self):
|
||||
for e in event.get():
|
||||
if e.type == QUIT: exit()
|
||||
if e.type is KEYDOWN:
|
||||
for i in self.inputs:
|
||||
if i.id == e.key:
|
||||
i.update("Down")
|
||||
if e.type is KEYUP:
|
||||
for i in self.inputs:
|
||||
if i.id == e.key:
|
||||
i.update("Up")
|
||||
if e.type is MOUSEBUTTONDOWN:
|
||||
for i in self.inputs:
|
||||
if i.id == e.button:
|
||||
i.update("Down")
|
||||
if e.type is MOUSEBUTTONUP:
|
||||
for i in self.inputs:
|
||||
if i.id == e.button:
|
||||
i.update("Up")
|
||||
|
||||
for i in self.inputs:
|
||||
i.notify()
|
||||
#chec for keyUpdates
|
BIN
games/tic-tac-toe/inputmanager.pyc
Normal file
BIN
games/tic-tac-toe/inputmanager.pyc
Normal file
Binary file not shown.
29
games/tic-tac-toe/keypress.py
Normal file
29
games/tic-tac-toe/keypress.py
Normal file
|
@ -0,0 +1,29 @@
|
|||
#KeyPress.py
|
||||
|
||||
#Should call method if a key was pressed
|
||||
#Variales: isPressed, actionPerformed
|
||||
#To make sure that the key only get's pressed once when holding a button down, Two variables are needed.
|
||||
#isPressed: If not pressed, call method on next keypress
|
||||
#actionPerformed: If method was called and key is still down, do not call method no more
|
||||
#Input instances will be allocated before gameplay, therefore: isPressed and actionPerformed will be initialized false.
|
||||
|
||||
#methods: init, update
|
||||
#update: Will be called when there is a change in this actual keys state. With respect to the input class behaviour,
|
||||
#update will evaluate if it's corresponding method should be called or not
|
||||
#param: state tells if the key is down or up.
|
||||
|
||||
|
||||
#Four possible preconditions: is not pressed and key is up. -> Set isPressed to false.
|
||||
# is not pressed and key is down. ->
|
||||
# is pressed and key is up.
|
||||
# is pressed and key is down.
|
||||
|
||||
from input import Input
|
||||
|
||||
class KeyPress(Input):
|
||||
def __init__(self, id, function):
|
||||
Input.__init__(self, id, function)
|
||||
|
||||
def update(self,state):
|
||||
if state == "Down":
|
||||
self.fun()
|
BIN
games/tic-tac-toe/keypress.pyc
Normal file
BIN
games/tic-tac-toe/keypress.pyc
Normal file
Binary file not shown.
47
games/tic-tac-toe/main.py
Normal file
47
games/tic-tac-toe/main.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from sys import exit
|
||||
#from pygame.image import load
|
||||
from pygame.display import set_mode, flip
|
||||
from pygame import init
|
||||
import pygame.event as event
|
||||
from pygame.mouse import get_pos
|
||||
from pygame import QUIT,K_UP
|
||||
from inputmanager import InputManager
|
||||
from tictactoeboard import TicTacToeBoard
|
||||
from point import Point
|
||||
|
||||
#inputmanager = None
|
||||
#screen = None
|
||||
#board = None
|
||||
|
||||
def main():
|
||||
global inputmanager,screen,board
|
||||
#init graphics
|
||||
init()
|
||||
size = (150,150)
|
||||
screen = set_mode(size)
|
||||
|
||||
#init game data
|
||||
nr_of_rectangles = 9
|
||||
board = TicTacToeBoard(nr_of_rectangles)
|
||||
#board.paint(screen)
|
||||
|
||||
#init input
|
||||
inputmanager = InputManager([
|
||||
("Mouse", 1, "Press", (lambda: board.make_turn(Point(get_pos()[0],get_pos()[1])))),
|
||||
("Key", K_UP, "Press", (lambda: print("Hello Keyboard!"))),
|
||||
])
|
||||
|
||||
|
||||
loop()
|
||||
|
||||
def loop():
|
||||
global inputmanager,screen,board
|
||||
while True:
|
||||
inputmanager.update()
|
||||
board.paint(screen)
|
||||
flip()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
9
games/tic-tac-toe/mousepress.py
Normal file
9
games/tic-tac-toe/mousepress.py
Normal file
|
@ -0,0 +1,9 @@
|
|||
from input import Input
|
||||
|
||||
class MousePress(Input):
|
||||
def __init__(self, id, function):
|
||||
Input.__init__(self, id, function)
|
||||
|
||||
def update(self,state):
|
||||
if state == "Down":
|
||||
self.fun()
|
BIN
games/tic-tac-toe/mousepress.pyc
Normal file
BIN
games/tic-tac-toe/mousepress.pyc
Normal file
Binary file not shown.
BIN
games/tic-tac-toe/o.png
Normal file
BIN
games/tic-tac-toe/o.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
12
games/tic-tac-toe/point.py
Normal file
12
games/tic-tac-toe/point.py
Normal file
|
@ -0,0 +1,12 @@
|
|||
from rectangle import Rectangle
|
||||
|
||||
class Point(object):
|
||||
def __init__(self, x, y):
|
||||
self.x = x
|
||||
self.y = y
|
||||
|
||||
def inside(self, rectangle):
|
||||
return ((self.x >= rectangle.x)
|
||||
and (self.x <= (rectangle.x + rectangle.width))
|
||||
and (self.y >= rectangle.y)
|
||||
and (self.y <= rectangle.y + rectangle.height))
|
BIN
games/tic-tac-toe/point.pyc
Normal file
BIN
games/tic-tac-toe/point.pyc
Normal file
Binary file not shown.
8
games/tic-tac-toe/rectangle.py
Normal file
8
games/tic-tac-toe/rectangle.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
|
||||
class Rectangle(object):
|
||||
def __init__(self, x, y, width, height):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
BIN
games/tic-tac-toe/rectangle.pyc
Normal file
BIN
games/tic-tac-toe/rectangle.pyc
Normal file
Binary file not shown.
32
games/tic-tac-toe/test_board.py
Normal file
32
games/tic-tac-toe/test_board.py
Normal file
|
@ -0,0 +1,32 @@
|
|||
import unittest
|
||||
from board import Board
|
||||
from rectangle import Rectangle
|
||||
|
||||
class TestBoard(unittest.TestCase):
|
||||
def setUp(self):
|
||||
nr_of_rectangles = 4
|
||||
dimensions = Rectangle(0, 0, 25, 35)
|
||||
self.b = Board(nr_of_rectangles, dimensions)
|
||||
|
||||
def test_rectangle_placemenets(self):
|
||||
#rectangle1: 0,0,25,35
|
||||
#rectangle2: 26,0,25,35
|
||||
#rectangle3: 0,36,25,35
|
||||
#rectangle4: 26,36,25,35
|
||||
|
||||
r0 = self.b.game_rectangles[0]
|
||||
r1 = self.b.game_rectangles[1]
|
||||
r2 = self.b.game_rectangles[2]
|
||||
r3 = self.b.game_rectangles[3]
|
||||
self.assertEqual(r0.x, 0)
|
||||
self.assertEqual(r0.y, 0)
|
||||
self.assertEqual(r1.x, 25)
|
||||
self.assertEqual(r1.y, 0)
|
||||
self.assertEqual(r2.x, 0)
|
||||
self.assertEqual(r2.y, 35)
|
||||
self.assertEqual(r3.x, 25)
|
||||
self.assertEqual(r3.y, 35)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
23
games/tic-tac-toe/test_gamerectangle.py
Normal file
23
games/tic-tac-toe/test_gamerectangle.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import unittest
|
||||
from gamerectangle import GameRectangle
|
||||
|
||||
|
||||
|
||||
class TestGameRectangle(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.index = 0
|
||||
self.x = 1
|
||||
self.y = 2
|
||||
self.width = 34
|
||||
self.height = 0.23
|
||||
self.gr = GameRectangle(self.index, self.x, self.y, self.width, self.height)
|
||||
def test_attributes(self):
|
||||
self.assertEqual(self.index, self.gr.index)
|
||||
self.assertEqual(self.gr.state, ' ')
|
||||
self.assertEqual(self.x, self.gr.x)
|
||||
self.assertEqual(self.y, self.gr.y)
|
||||
self.assertEqual(self.width, self.gr.width)
|
||||
self.assertEqual(self.height, self.gr.height)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
27
games/tic-tac-toe/test_rectangle.py
Normal file
27
games/tic-tac-toe/test_rectangle.py
Normal file
|
@ -0,0 +1,27 @@
|
|||
import unittest
|
||||
from rectangle import Rectangle
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
x = 1
|
||||
y = 2
|
||||
width = 34
|
||||
height = 0.23
|
||||
r = Rectangle(x, y, width, height)
|
||||
|
||||
class TestRectangle(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.x = 1
|
||||
self.y = 2
|
||||
self.width = 34
|
||||
self.height = 0.23
|
||||
self.r = Rectangle(self.x, self.y, self.width, self.height)
|
||||
def test_attributes(self):
|
||||
self.assertEqual(self.x, self.r.x)
|
||||
self.assertEqual(self.y, self.r.y)
|
||||
self.assertEqual(self.r.width, self.r.width)
|
||||
self.assertEqual(self.r.height, self.r.height)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
51
games/tic-tac-toe/tictactoeboard.py
Normal file
51
games/tic-tac-toe/tictactoeboard.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
from board import Board
|
||||
from rectangle import Rectangle
|
||||
from point import Point
|
||||
from pygame.image import load
|
||||
from pygame.rect import Rect
|
||||
from pygame import Surface
|
||||
|
||||
#inherits Board.
|
||||
#Used for displaying the board on the screen and interact with it
|
||||
#thus images are needed for the various rectangles
|
||||
#as well as a paint function
|
||||
#Should be updated after every valid player move
|
||||
|
||||
class TicTacToeBoard(Board):
|
||||
def __init__(self, nr_of_rectangles):
|
||||
self.image_e = load("e.png")
|
||||
self.image_x = load("x.png")
|
||||
self.image_o = load("o.png") #TODO add the o image
|
||||
dimensions = Rectangle(0, 0, self.image_x.get_width(),
|
||||
self.image_x.get_height())
|
||||
self.players_turn = 0
|
||||
Board.__init__(self, nr_of_rectangles, dimensions)
|
||||
|
||||
def paint(self, table_image):
|
||||
for game_rectangle in self.game_rectangles:
|
||||
x = game_rectangle.x
|
||||
y = game_rectangle.y
|
||||
w = game_rectangle.width
|
||||
h = game_rectangle.height
|
||||
|
||||
image = None
|
||||
if game_rectangle.state == ' ':
|
||||
image = self.image_e
|
||||
elif game_rectangle.state == 'x':
|
||||
image = self.image_x
|
||||
elif game_rectangle.state == 'o':
|
||||
image = self.image_o
|
||||
print(game_rectangle.state)
|
||||
table_image.blit(image, Rect(x, y, w, h))
|
||||
|
||||
def make_turn(self, mouse_point):
|
||||
for game_rectangle in self.game_rectangles:
|
||||
if (mouse_point.inside(game_rectangle) and
|
||||
game_rectangle.state == ' '):
|
||||
if self.players_turn == 0:
|
||||
game_rectangle.state = 'x'
|
||||
elif self.players_turn == 1:
|
||||
game_rectangle.state = 'o'
|
||||
self.players_turn = (self.players_turn + 1) % 2
|
||||
|
||||
|
BIN
games/tic-tac-toe/tictactoeboard.pyc
Normal file
BIN
games/tic-tac-toe/tictactoeboard.pyc
Normal file
Binary file not shown.
BIN
games/tic-tac-toe/x.png
Normal file
BIN
games/tic-tac-toe/x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7.5 KiB |
Reference in a new issue