From d26c7a3a8842fdef1fb2ae46dfb173966eda0fb6 Mon Sep 17 00:00:00 2001 From: Sebastian Lohff Date: Sun, 1 Dec 2013 22:41:44 +0100 Subject: [PATCH] GameState loading/saving works --- game.py | 49 ++++++++++++++++++++++++++++++++++++------------- gamestate.py | 51 +++++++++++++++++++++++++++++++++++++++++++++------ player.py | 21 +++++++++++++++++++++ seopardy.py | 17 ++++++++++++++++- 4 files changed, 118 insertions(+), 20 deletions(-) diff --git a/game.py b/game.py index cda8447..0e988b8 100644 --- a/game.py +++ b/game.py @@ -13,9 +13,11 @@ class SeopardyGame(QtGui.QWidget): self.questions = questions self.gamestate = gamestate #self.players = [Player.gen_player(i, parent=self) for i in range(1, 4)] - self.players = [] - for i in range(3): - self.add_player() + self.players = self.gamestate.get_players() + if len(self.players) == 0: + for i in range(3): + self.add_player() + self.currentPlayer = None self._inOtherWindow = False @@ -23,7 +25,8 @@ class SeopardyGame(QtGui.QWidget): self.showFullScreen() # initiate starting the game! - QtCore.QCoreApplication.postEvent(self, GameStartEvent()) + if self.gamestate.get_state() == "start": + QtCore.QCoreApplication.postEvent(self, GameStartEvent()) def add_player(self): self.players.append(Player.gen_player(len(self.players)+1, self)) @@ -40,22 +43,22 @@ class SeopardyGame(QtGui.QWidget): # create board #board = QtGui.QGridLayout(6, len(self.questions.get_sections())) - board = QtGui.QGridLayout() - board.setSizeConstraint(QtGui.QLayout.SetMaximumSize) + self.board = QtGui.QGridLayout() + self.board.setSizeConstraint(QtGui.QLayout.SetMaximumSize) for i, sec in enumerate(self.questions.get_sections()): seclabel = QtGui.QLabel(sec, alignment=QtCore.Qt.AlignCenter) seclabel.setStyleSheet("QLabel { font-size: 30px; }") - board.addWidget(seclabel, 0, i) + self.board.addWidget(seclabel, 0, i) for j in range(5): b = QtGui.QPushButton(str((j+1)*100)) b.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) b.setAutoDefault(False) - b.setStyleSheet("QPushButton { font-size: 60px; }") + #b.setStyleSheet("QPushButton { font-size: 60px; }") b.installEventFilter(FocusKeyGrabber(i+1, j+1, self)) b.clicked.connect(lambda sec=sec, j=j: self.go_to_question(sec, j+1)) - board.addWidget(b, j+1, i) - layout.addLayout(board) - self.board = board + self.board.addWidget(b, j+1, i) + self._restyle_button(sec, j+1, self.gamestate.get_answers(sec, j+1)) + layout.addLayout(self.board) # create player bar self.playerBar = QtGui.QHBoxLayout() @@ -81,6 +84,8 @@ class SeopardyGame(QtGui.QWidget): x = PlayerStartWindow(self.players, self) x.exec_() self._inOtherWindow = False + self.gamestate.set_state("playing") + self.gamestate.save() return True else: @@ -136,6 +141,7 @@ class SeopardyGame(QtGui.QWidget): self._set_player_points(newAnswers) self.gamestate.set_answers(section, number, newAnswers) self._restyle_button(section, number, newAnswers) + self.gamestate.save() def show_victory_window(self): self._inOtherWindow = True @@ -176,6 +182,23 @@ class SeopardyGame(QtGui.QWidget): # restyle the button self._restyle_button(section, number, answers) + # safe current gamestate + self.gamestate.save() + + # check if we are done + foundUnanswered = False + for sec in self.questions.get_sections(): + for j in range(5): + boardAnswers = self.gamestate.get_answers(sec, j+1) + if boardAnswers is None or not answers.is_answered(): + foundUnanswered = True + if foundUnanswered: + break + + if not foundUnanswered: + self.show_victory_window() + + def _set_player_points(self, answers, rollback=False): for i, (player, correct) in enumerate(answers.get_tries()): prefix = 1 @@ -199,8 +222,8 @@ class SeopardyGame(QtGui.QWidget): btnstr = "" btncolor = None - if not answers.is_answered(): - btn.setText(str(answers.points())) + if answers is None or not answers.is_answered(): + btn.setText(str(qno*100)) btn.setStyleSheet("QPushButton { font-size: 60px; }") return diff --git a/gamestate.py b/gamestate.py index eba2a49..95b29e6 100644 --- a/gamestate.py +++ b/gamestate.py @@ -1,6 +1,8 @@ from __future__ import print_function +import datetime import os +import yaml from collections import defaultdict class GameState(object): @@ -10,17 +12,30 @@ class GameState(object): savedir: dir to save gamestate into, should be overwritable board: dict of section with list of questions """ + GAMESTATE = ["start", "playing", "end"] + def __init__(self, savedir): self.savedir = savedir - self._board = defaultdict(lambda: [None]*5) - self._players = None + #self._board = defaultdict(lambda: [None]*5) + self._board = defaultdict(_mk_empty_board_column) + self._players = [] + self._state = self.GAMESTATE[0] if not os.path.exists(self.savedir): os.mkdir(self.savedir) elif not os.path.isdir(self.savedir): raise ValueError("'%s' is not a directory but something else!" % self.savedir) + + def set_state(self, state): + if state not in self.GAMESTATE: + raise ValueError("'%s' is not a valid gamestate, valid choices are %s" % (state, self.GAMESTATE)) + self._state = state + + def get_state(self): + return self._state + def set_answers(self, section, qnumber, answers): self._board[section][qnumber-1] = answers @@ -28,14 +43,38 @@ class GameState(object): return self._board[section][qnumber-1] def set_players(self, players): - self.players = players + self._players = players + + def get_players(self): + return self._players + + def _make_structure(self): + return { + "state": self._state, + "players": self._players, + "board": self._board + } def save(self): - pass + fname = datetime.datetime.now().strftime("seopardy.%Y-%m-%d-_%H:%M:%S.save") + fstream = open("%s/%s" % (self.savedir, fname), "w") + yaml.dump(self._make_structure(), fstream) + fstream.close() @classmethod - def load(clazz, fpath): - pass + def load(clazz, sstream, savedir): + gs = clazz(savedir) + data = yaml.load(sstream) + gs._state = data["state"] + gs._players = data["players"] + gs._board = data["board"] + + return gs + +def _mk_empty_board_column(): + """ We can't use a lambda here, as we want the default dict to be + easily serializeable by yaml. """ + return [None]*5 class QuestionAnswers(object): def __init__(self, section, qnumber, dj_points=None): diff --git a/player.py b/player.py index d36af1e..0b775cb 100644 --- a/player.py +++ b/player.py @@ -1,5 +1,6 @@ from __future__ import print_function +import yaml from PySide import QtGui, QtCore class Player(QtGui.QWidget): @@ -9,6 +10,7 @@ class Player(QtGui.QWidget): ("Baz", QtGui.QColor(0, 0, 255)), ("Blubb", QtGui.QColor(0, 255, 255)), ("Murr", QtGui.QColor(255, 0, 255)), + ("Maunz", QtGui.QColor(255, 255, 0)), ] def __init__(self, name, color, points=0, parent=None): @@ -46,6 +48,21 @@ class Player(QtGui.QWidget): return clazz(*clazz.DEFAULT_PLAYERS[num-1], parent=parent) + @classmethod + def _yaml_representer(clazz, dumper, data): + #def __init__(self, name, color, points=0, parent=None): + return dumper.represent_mapping(u'!player', { + 'name': data.name, + 'points': data.points, + 'color': data.color.name() + }) + + @classmethod + def _yaml_constructor(clazz, loader, node): + pdict = loader.construct_mapping(node) + pdict["color"] = QtGui.QColor(pdict["color"]) + return Player(**pdict) + class ButtonEvent(QtCore.QEvent): eventType = QtCore.QEvent.Type(QtCore.QEvent.registerEventType()) @@ -60,3 +77,7 @@ class ButtonEvent(QtCore.QEvent): return self.playerno nobodyColor = QtGui.QColor(128, 128, 128) + +# yaml dumpers/loaders +yaml.add_representer(Player, Player._yaml_representer) +yaml.add_constructor(u'!player', Player._yaml_constructor) diff --git a/seopardy.py b/seopardy.py index f005dcd..6c6a3ec 100755 --- a/seopardy.py +++ b/seopardy.py @@ -31,12 +31,27 @@ if __name__ == '__main__': os.mkfifo(args.fifo) except OSError as e: print("Error: Could not create fifo: %s" % (str(e),), file=sys.stderr) + sys.exit(1) questions = Questions(args.questions) - gamestate = GameState(args.savedir) # start gui app = QtGui.QApplication([]) + + # create or load gamestate + gamestate = None + if args.gamestate: + sstream = None + try: + sstream = open(args.gamestate, "r") + except IOError as e: + print("Error: Could not load gamestate: %s" % (str(e),), file=sys.stderr) + sys.exit(1) + gamestate = GameState.load(sstream, args.savedir) + else: + gamestate = GameState(args.savedir) + + # create board board = SeopardyGame(questions, gamestate) board.show() app.exec_()