https://github.com/Sohieb/reversi-game
main.py
import pygame
import random
import view
import board
import common
from view import *
from board import *
from common import *
class game_manager:
"""
The Main class which handle the overall game control
"""
def __init__(self):
## create a veiw and a model objects
self.window = view.game_interface()
self.board = board.game_model()
## Show the intro screen and get the initial game parameters
self.game_mode = self.window.intro_screen()
self.current_playing = 0 # 0 -> player_1, 1 -> player_2
# The main game function handling the game loop
def play(self):
pygame.time.wait(300)
clock = pygame.time.Clock()
while True:
if self.board.is_game_ended():
count_white, count_black = self.board.get_cell_count()
if count_black > count_white:
self.winner = "BLACK"
elif count_white > count_black:
self.winner = "WHITE"
else:
self.winner = "TIE"
self.next_action = self.window.result_screen(self.winner)
pygame.time.wait(300)
if self.next_action == "Continue":
self.board.reset_game_board()
self.game_mode = self.window.intro_screen()
else:
break
if self.game_mode[self.current_playing] == "Human":
valid_moves = self.board.get_valid_moves(self.current_playing)
clicked_cell = self.window.game_screen(self.board.grid, True)
while clicked_cell[0] * 8 + clicked_cell[1] not in valid_moves:
clicked_cell = self.window.game_screen(self.board.grid, True)
self.board.make_move(clicked_cell, self.current_playing)
else: ## current player is the computer actor
pygame.time.wait(300)
if self.game_mode[2] == "Easy": # Random move
##########print "Easy"
valid_moves = self.board.get_valid_moves(self.current_playing)
rand_move = random.randint(0, len(valid_moves) - 1)
clicked_cell = (valid_moves[rand_move] // common.SIDE_LEN, \
valid_moves[rand_move] % common.SIDE_LEN)
self.board.make_move(clicked_cell, self.current_playing)
# else: #TODO # Intelligent move
##########print "Hard"
self.current_playing = 1 - self.current_playing ## switch to te next player
self.window.game_screen(self.board.grid, False)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
clock.tick(60)
# The main Function
def main():
game = game_manager() ## create a game instance
game.play() ## run the game
if __name__ == '__main__':
main()
view.py
import pygame
import time
import common
from common import *
class game_interface:
"""
The GUI class responsible for the game graphics
and all (input / output) interactions.
"""
def __init__(self):
pygame.init()
# main colors
self.BLACK_COLOR = (0, 0, 0)
self.WHITE_COLOR = (255, 255, 255)
self.BACKGROUND_COLOR = (0, 140, 90)
self.BUTTON_INACTIVE_COLOR = (0, 250, 150)
self.BUTTON_ACTIVE_COLOR = (0, 100, 100)
self.BORDER_COLOR = (205, 133, 63) ## peru -> #CD853F
# display parameters
self.SCREEN_SIZE = (700, 500)
self.BOARD_SIZE = 400
self.BORDER_SIZE = 20
self.CELL_SIZE = 50
# start the game screen
self.game_display = pygame.display.set_mode(self.SCREEN_SIZE)
pygame.display.set_caption("Reversi GAME")
def intro_screen(self):
"""
Show the intro screen and return either
user choose 1 player or two players mode
"""
while True:
self.game_display.fill(self.BACKGROUND_COLOR)
game_title_font = pygame.font.SysFont('comicsansms', 100)
game_title = game_title_font.render("Reversi Game", True, self.WHITE_COLOR)
game_title_pos = game_title.get_rect()
game_title_pos.center = (self.SCREEN_SIZE[0] // 2, self.SCREEN_SIZE[1] // 2 - 50)
self.game_display.blit(game_title, game_title_pos)
mode1_clicked = self.add_button("1 Player", 100, 350, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
mode2_clicked = self.add_button("2 Player", 300, 350, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
exit_game_clicked = self.add_button("Exit", 500, 350, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
if mode1_clicked == True:
diff_level = self.get_diff_level()
return ("Human", "Computer", diff_level)
if mode2_clicked == True:
return ("Human", "Human", "NONE")
if exit_game_clicked == True:
pygame.quit()
quit()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
def result_screen(self, winner):
"""
Show the result screen and return either
continue or it exits the game
"""
while True:
self.game_display.fill(self.BACKGROUND_COLOR)
game_title_font = pygame.font.SysFont('comicsansms', 100)
if winner != "TIE":
game_title = game_title_font.render(winner + " Wins", True, self.WHITE_COLOR)
else:
game_title = game_title_font.render("TIE", True, self.WHITE_COLOR)
game_title_pos = game_title.get_rect()
game_title_pos.center = (self.SCREEN_SIZE[0] // 2, self.SCREEN_SIZE[1] // 2 - 50)
self.game_display.blit(game_title, game_title_pos)
mode1_clicked = self.add_button("Continue", 200, 350, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
exit_game_clicked = self.add_button("Exit", 400, 350, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
if mode1_clicked == True:
return "Continue"
if exit_game_clicked == True:
return "Exit"
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
def get_diff_level(self):
"""
Show the diff levels and return the selected
"""
while True:
self.game_display.fill(self.BACKGROUND_COLOR)
game_title_font = pygame.font.SysFont('comicsansms', 100)
game_title = game_title_font.render("Reversi Game", True, self.WHITE_COLOR)
game_title_pos = game_title.get_rect()
game_title_pos.center = (self.SCREEN_SIZE[0] // 2, self.SCREEN_SIZE[1] // 2 - 50)
self.game_display.blit(game_title, game_title_pos)
diff_easy = self.add_button("Easy", 300, 300, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
diff_hard = self.add_button("Hard", 300, 400, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
if diff_easy == True:
return "Easy"
if diff_hard == True:
return "Hard"
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
def add_button(self, message, start_X, start_Y, width, height, inactive_col, active_col):
mouse_pos = pygame.mouse.get_pos()
mouse_click = pygame.mouse.get_pressed()
is_clicked = False
draw_color = inactive_col
if start_X + width > mouse_pos[0] > start_X and start_Y + height > mouse_pos[1] > start_Y:
draw_color = active_col
if mouse_click[0] == 1:
is_clicked = True
pygame.draw.rect(self.game_display, draw_color, (start_X, start_Y, width, height))
button_text_font = pygame.font.SysFont('comicsansms', 20)
button_text = button_text_font.render(message, True, self.BLACK_COLOR)
button_text_pos = button_text.get_rect()
button_text_pos.center = (start_X + width // 2, start_Y + height // 2)
self.game_display.blit(button_text, button_text_pos)
return is_clicked
def get_pressed_cell(self):
pygame.event.get() ## To flush
mouse_pos = pygame.mouse.get_pos()
mouse_click = pygame.mouse.get_pressed()
if mouse_click[0] == 0:
return (-1, -1)
if 150 + self.BOARD_SIZE > mouse_pos[0] > 150 and 50 + self.BOARD_SIZE > mouse_pos[1] > 50:
pos_X = mouse_pos[0] - 150
pos_Y = mouse_pos[1] - 50
pos_X = pos_X // self.CELL_SIZE
pos_Y = pos_Y // self.CELL_SIZE
return (pos_X, pos_Y)
return (-1, -1)
def game_screen(self, grid, need_click):
"""
Main game screen
"""
## (start_X, start_Y, width, height)
upper_border = (150 - self.BORDER_SIZE, 50 - self.BORDER_SIZE, \
self.BOARD_SIZE + 2 * self.BORDER_SIZE, self.BORDER_SIZE)
right_border = (self.SCREEN_SIZE[0] - 150, 50 - self.BORDER_SIZE, \
self.BORDER_SIZE, self.BOARD_SIZE + 2 * self.BORDER_SIZE)
lower_border = (150 - self.BORDER_SIZE, self.SCREEN_SIZE[1] - 50, \
self.BOARD_SIZE + 2 * self.BORDER_SIZE, self.BORDER_SIZE)
left_border = (150 - self.BORDER_SIZE, 50 - self.BORDER_SIZE, \
self.BORDER_SIZE, self.BOARD_SIZE + 2 * self.BORDER_SIZE)
horizontal_lines_start_X = 150
horizontal_lines_start_Y = 100
horizontal_lines_end_X = 700 - 150
horizontal_lines_end_Y = 100
vertical_lines_start_X = 200
vertical_lines_start_Y = 50
vertical_lines_end_X = 200
vertical_lines_end_Y = 500 - 50
while True:
self.game_display.fill(self.BACKGROUND_COLOR)
my_line_start_X = horizontal_lines_start_X
my_line_start_Y = horizontal_lines_start_Y
my_line_end_X = horizontal_lines_end_X
my_line_end_Y = horizontal_lines_end_Y
for i in range(common.SIDE_LEN - 1):
pygame.draw.line(self.game_display, self.BLACK_COLOR, \
(my_line_start_X, my_line_start_Y), (my_line_end_X, my_line_end_Y), 2)
my_line_start_Y += self.CELL_SIZE
my_line_end_Y += self.CELL_SIZE
my_line_start_X = vertical_lines_start_X
my_line_start_Y = vertical_lines_start_Y
my_line_end_X = vertical_lines_end_X
my_line_end_Y = vertical_lines_end_Y
for i in range(common.SIDE_LEN - 1):
pygame.draw.line(self.game_display, self.BLACK_COLOR, \
(my_line_start_X, my_line_start_Y), (my_line_end_X, my_line_end_Y), 2)
my_line_start_X += self.CELL_SIZE
my_line_end_X += self.CELL_SIZE
pygame.draw.rect(self.game_display, self.BORDER_COLOR, upper_border)
pygame.draw.rect(self.game_display, self.BORDER_COLOR, right_border)
pygame.draw.rect(self.game_display, self.BORDER_COLOR, lower_border)
pygame.draw.rect(self.game_display, self.BORDER_COLOR, left_border)
for i in range(common.SIDE_LEN):
for j in range(common.SIDE_LEN):
if grid[i][j] == common.EMPTY_CELL:
continue
circle_color = self.BLACK_COLOR
if grid[i][j] == common.WHITE_CELL:
circle_color = self.WHITE_COLOR
pos_X = 150 + self.CELL_SIZE * i + self.CELL_SIZE / 2
pos_Y = 50 + self.CELL_SIZE * j + self.CELL_SIZE / 2
pygame.draw.circle(self.game_display, circle_color, (pos_X, pos_Y), 20)
exit_game_clicked = self.add_button("Exit", 585, 400, 100, 50, \
self.BUTTON_INACTIVE_COLOR, self.BUTTON_ACTIVE_COLOR)
if exit_game_clicked == True:
pygame.quit()
quit()
clicked_cell = self.get_pressed_cell()
if clicked_cell[0] != -1 and clicked_cell[1] != -1:
return clicked_cell
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
if need_click == False:
break
board.py
import common
from common import *
class game_model:
"""
The game structure, data and logic
"""
def __init__(self):
## common parameters
self.BLACK_CELL = common.BLACK_CELL
self.WHITE_CELL = common.WHITE_CELL
self.EMPTY_CELL = common.EMPTY_CELL
self.SIDE_LEN = common.SIDE_LEN
## create the game grid & initialize it with empty cells
self.grid = [[0 for x in range(self.SIDE_LEN)] for y in range(self.SIDE_LEN)]
for i in range(self.SIDE_LEN):
for j in range(self.SIDE_LEN):
self.grid[i][j] = self.EMPTY_CELL
## set the initial 4 center cells
self.grid[3][3] = self.WHITE_CELL
self.grid[4][4] = self.WHITE_CELL
self.grid[3][4] = self.BLACK_CELL
self.grid[4][3] = self.BLACK_CELL
self.valid_moves = []
def reset_game_board(self):
## create the game grid & initialize it with empty cells
self.grid = [[0 for x in range(self.SIDE_LEN)] for y in range(self.SIDE_LEN)]
for i in range(self.SIDE_LEN):
for j in range(self.SIDE_LEN):
self.grid[i][j] = self.EMPTY_CELL
## set the initial 4 center cells
self.grid[3][3] = self.WHITE_CELL
self.grid[4][4] = self.WHITE_CELL
self.grid[3][4] = self.BLACK_CELL
self.grid[4][3] = self.BLACK_CELL
def set_cell(self, pos, color):
self.grid[pos[0]][pos[1]] = color
def get_grid(self):
return self.grid
def get_valid_moves(self, turn):
"""
Get and return the valid moves for the current player.
Each move is represented as a one integer value which
range from 0 to 63, where this number equals i * 8 + j
"""
valid_moves = []
for i in range(self.SIDE_LEN):
for j in range(self.SIDE_LEN):
if self.grid[i][j] != self.EMPTY_CELL:
continue
for k in range(common.DIR_COUNT):
count_my_cells = 0
count_other_cells = 0
cur_X = i + common.dx[k]
cur_Y = j + common.dy[k]
while self.SIDE_LEN > cur_X >= 0 and self.SIDE_LEN > cur_Y >= 0:
if self.grid[cur_X][cur_Y] == 1 - turn:
count_other_cells += 1
cur_X += dx[k]
cur_Y += dy[k]
elif self.grid[cur_X][cur_Y] == turn:
count_my_cells += 1
break
else:
break
if count_my_cells > 0 and count_other_cells > 0:
valid_moves.append(i * self.SIDE_LEN + j)
break
self.valid_moves = valid_moves
return self.valid_moves
def is_game_ended(self):
"""
Returns true if the game already ended and false otherwise
"""
count_white, count_black = self.get_cell_count()
# One of them already lost all his cells
if count_white == 0 or count_black == 0:
return True
# The board all is full (no empty cells)
if count_white + count_black == self.SIDE_LEN * self.SIDE_LEN:
return True
# If no one can do any more moves
if self.get_valid_moves(self.WHITE_CELL) == [] and self.get_valid_moves(self.BLACK_CELL) == []:
return True
# Otherwise the game still running
return False
def get_cell_count(self):
"""
Returns 2 parameters, which is the number of
white and the number of black cells exists on the board
"""
white = 0
black = 0
for i in range(self.SIDE_LEN):
for j in range(self.SIDE_LEN):
if self.grid[i][j] == self.WHITE_CELL:
white += 1
elif self.grid[i][j] == self.BLACK_CELL:
black += 1
return white, black
def make_move(self, move_pos, turn):
# TO DO
i = move_pos[0]
j = move_pos[1]
for k in range(common.DIR_COUNT):
count_my_cells = 0
count_other_cells = 0
cur_X = i + common.dx[k]
cur_Y = j + common.dy[k]
while self.SIDE_LEN > cur_X >= 0 and self.SIDE_LEN > cur_Y >= 0:
if self.grid[cur_X][cur_Y] == 1 - turn:
count_other_cells += 1
cur_X += dx[k]
cur_Y += dy[k]
elif self.grid[cur_X][cur_Y] == turn:
count_my_cells += 1
break
else:
break
if count_my_cells > 0 and count_other_cells > 0:
cur_X = i + common.dx[k]
cur_Y = j + common.dy[k]
while self.SIDE_LEN > cur_X >= 0 and self.SIDE_LEN > cur_Y >= 0:
if self.grid[cur_X][cur_Y] == 1 - turn:
self.grid[cur_X][cur_Y] = turn
cur_X += dx[k]
cur_Y += dy[k]
else:
break
self.grid[i][j] = turn
common.py
## common parameters used in the game
BLACK_CELL = 0
WHITE_CELL = 1
EMPTY_CELL = -1
SIDE_LEN = 8
## parameters allow the board class to get the
## valid moves and update the grid after a move
DIR_COUNT = 8
dx = [-1, -1, -1, 0, 0, 1, 1, 1]
dy = [-1, 0, 1, -1, 1, -1, 0, 1]