cgame.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. #!/usr/bin/env python
  2. """
  3. Class defenition for Carcassonne score keeping system.
  4. Copyright 2018 George C. Privon
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <http://www.gnu.org/licenses/>.
  15. """
  16. import re as _re
  17. import sys as _sys
  18. from datetime import datetime as _datetime
  19. import sqlite3 as _sqlite3
  20. import numpy as _np
  21. class cgame:
  22. """
  23. Carcassonne game object
  24. """
  25. def __init__(self):
  26. """
  27. Initialize some variables and set up a game
  28. """
  29. self.commands = [('n', 'next round'),
  30. ('r', 'record score and advance turn'),
  31. ('t', 'advance turn, no score'),
  32. ('b', 'additional turn for a player due to a builder'),
  33. ('e', 'end game (or play if already in postgame scoring'),
  34. ('s', '(current) score and game status'),
  35. ('?', 'print help')]
  36. self.conn = sqlite3.connect('CarcassonneScore.db')
  37. self.cur = self.conn.cursor()
  38. setupGame()
  39. def showCommands(self):
  40. """
  41. Print out a list of valid commands for in-game play.
  42. """
  43. _sys.stderr.write('Possible commands:\n')
  44. for entry in self.commands:
  45. _sys.stderr.write('\t' + entry[0] + ': ' + entry[1] + '\n')
  46. def setupGame(self):
  47. """
  48. Initialize a game
  49. """
  50. # check to see if there's a game which has not yet been finished (i.e., has a starttime but no endtime). If there is, resume it. Otherwise:
  51. # generate a new game ID and enter a start time
  52. # get a list of players (from database list)
  53. # have user input which expansions are being used
  54. time = _datetime.utcnow().strftime("%Y-%m-%dT%H:%M")
  55. # insert this into the database
  56. dbplayers = getPlayers()
  57. for dbplayer in dbplayers:
  58. print("{0:d}) ".format(dbplayer[0]) + dbplayer[1])
  59. playerinput = input("Please list the IDs for the players in this game (in order of play): ")
  60. playerIDs = [int(x) for x in player.input.split()]
  61. self.players = []
  62. for playerID in playerIDs:
  63. for dbplayer in dbplayers:
  64. if playerID == dbplayer[0]:
  65. self.players.append((playerID, dbplayer[1]))
  66. # general information
  67. self.gameID =
  68. self.expansionIDs =
  69. # game state information
  70. self.state = 0 # 0 for main game, 1 for postgame, 2 for ended game
  71. self.ntile = 1 # number of tiles played
  72. self.nbuilder = 0 # number of tiles placed due to builders
  73. def getPlayers(self):
  74. """
  75. Get a list of possible players from the database
  76. """
  77. players = self.cur.execute('''SELECT * FROM players''').fetchall()
  78. if not len(players):
  79. _sys.stderr.write("Error: players table empty. Exiting.\n")
  80. _sys.exit(-1)
  81. return players
  82. def recordScore(gameID, playerIDs, expansionIDs, cround, state):
  83. """
  84. Record a score event in the game
  85. """
  86. if state:
  87. ingame = 0
  88. pname = getPlayerNames(playerIDs)
  89. # for i, pname in enumerate(pname):
  90. pID = input("")
  91. # if the builder was used
  92. BUILDERUSED = True
  93. advanceTurn(cmdtime, gameID, playerID, nturn, builder=BUILDERUSED)
  94. return 0
  95. def advanceTurn(self, builder=False):
  96. """
  97. Make a new entry in the turns table
  98. """
  99. command = '''INSERT INTO turns VALUES ({0:d}, {1:d}, '''.format(self.gameID, self.nturn)
  100. command = command + cmdtime
  101. if builder:
  102. bID = 1
  103. else:
  104. bID = 0
  105. # compute playerID based on the turn number minus nbuilders / number of players
  106. playerID = playerIDs[(nturns - nbuilder) / lnen(playerIDs)]
  107. command = command + ', {0:d}, {1:d})'.format(bID, playerID)
  108. c.execute(command)
  109. self.nturn += 1
  110. if builder:
  111. self.nbuilder += 1
  112. def runGame(self):
  113. """
  114. Main routine for entering games
  115. """
  116. # here wait for input for scores, advancing to next round, or completion of game
  117. # for each step of entry, present a series of options, based on the list
  118. # of playerIDs and expansions
  119. while self.state < 2:
  120. # set up prompt based on current round
  121. if self.state:
  122. prompt = "postgame > "
  123. else:
  124. prompt = "round: {0:d}, turn: {1:d} > ".format(1 + _np.floor((self.nturn-self.nbuilder) / len(self.playerIDs)),
  125. self.nturn-self.nbuilder)
  126. try:
  127. text = input(prompt)
  128. except (EOFError, KeyboardInterrupt):
  129. _sys.stderr.write('Improper input. Please retry\n')
  130. showCommands()
  131. if _re.match('e', cmd, _re.IGNORECASE):
  132. advanceState()
  133. elif _re.match('s', cmd, _re.IGNORECASE):
  134. printStatus(tilestats=True)
  135. elif _re.match('n', cmd, _re.IGNORECASE):
  136. advanceTurn()
  137. elif _re.match('r', cmd, _re.IGNORECASE):
  138. recordScore()
  139. elif _re.match('t', cmd, _re.IGNORECASE):
  140. advanceTurn(builder=False)
  141. elif _re.match('b', cmd, _re.IGNORECASE):
  142. advanceTurn(builder=True)
  143. elif _re.match('?'. cmd, _re.IGNORECASE):
  144. showCommands()
  145. else:
  146. _sys.stderr.write('Command not understood. Please try again.\n')
  147. showCommands()
  148. if state == 2:
  149. #game is over. write end time to the games table
  150. time = _datetime.utcnow().strftime("%Y-%m-%dT%H:%M")
  151. c.execute('''UPDATE games SET endtime = "''' + time + '''" WHERE gameID = ''' + str(gameID))
  152. conn.commit()
  153. printStatus(tilestats=False)
  154. #### Is there a way to capture "ineffective" uses? For example,
  155. #### meeples that don't score points because they end up in a meadow that's
  156. #### controled by someone else?
  157. return 0
  158. def printStatus(self, tilestats=False):
  159. """
  160. Print the total score (current or final) for the specified gameID
  161. """
  162. for playerID in self.playerIDs:
  163. pname = c.execute('SELECT name FROM players WHERE playerID={0:d}'.format(playerID[0])).fetchall()[0]
  164. a = c.execute('SELECT points FROM scores WHER gameID={0:d} and playerID={1:d}'.format(self.gameID, playerID[0]))
  165. res = a.fetchall()
  166. score = _np.sum(res)
  167. print(pname + ': {0:d}'.format(score))
  168. print("{0:d} tiles played out of {1:d} total ({2:d} remaining).".format(self.ntiles,
  169. self.totaltiles,
  170. self.totaltiles - self.ntiles)