Storycraft - collaborative nonsense story writing
this module implements a game where players write a line in a story, probably a nonsensical one, a couple lines at a time. once the player who started the story has written something, the last line is passed along to someone else in the game, who continues the story --- or disregards the small bit of context entirely and writes their own thing. eventually you get a story like this: line 1 by user 1 line 2 by user 1 line 3 by user 2 (who only read line 2) line 4 by user 2 line 5 by user 3 (who only read line 4) ... conceptually, that's the idea of the game. the code itself is still a bit rough around the edges, but i can bang through a game by myself. it needs some robustification, but it's fairly well documented and the module does try to provide some clues to IRC while you're playing. config option explanations, more such options, etc. to come. critically important is a way to get completed stories out of the bot, of course. more to come, i'll shut up now and commit.
This commit is contained in:
parent
649e183337
commit
90d38c1741
|
@ -0,0 +1,855 @@
|
|||
"""
|
||||
Storycraft - collaborative nonsense story writing
|
||||
Copyright (C) 2011 Brian S. Stephan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
import random
|
||||
import re
|
||||
import sqlite3
|
||||
|
||||
from Module import Module
|
||||
|
||||
__author__ = "Brian S. Stephan"
|
||||
__copyright__ = "Copyright 2011, Brian S. Stephan"
|
||||
__credits__ = ["Brian S. Stephan", "Mike Bloy", "#lh"]
|
||||
__license__ = "GPL"
|
||||
__version__ = "0.1"
|
||||
__maintainer__ = "Brian S. Stephan"
|
||||
__email__ = "bss@incorporeal.org"
|
||||
__status__ = "Development"
|
||||
|
||||
class Storycraft(Module):
|
||||
|
||||
"""Play Storycraft, the game of varying authors adding to a story mostly devoid of context.
|
||||
|
||||
This is a game for people who like making up facts, insulting each other, telling
|
||||
insane stories, making in-jokes, and generally having a good time writing what can
|
||||
fairly only be described as pure nonsense. It does not assume that all players are
|
||||
available at any given time, so games may take a while, but the module allows for
|
||||
concurrency in the number of games running.
|
||||
"""
|
||||
|
||||
class StorycraftGame():
|
||||
|
||||
"""Track a storycraft game details."""
|
||||
|
||||
pass
|
||||
|
||||
class StorycraftPlayer():
|
||||
|
||||
"""Track a storycraft player details."""
|
||||
|
||||
pass
|
||||
|
||||
class StorycraftLine():
|
||||
|
||||
"""Track a storycraft line details."""
|
||||
|
||||
pass
|
||||
|
||||
class StorycraftSettings():
|
||||
|
||||
"""Track the settings for the server."""
|
||||
|
||||
pass
|
||||
|
||||
def __init__(self, irc, config, server):
|
||||
"""Set up trigger regexes."""
|
||||
|
||||
Module.__init__(self, irc, config, server)
|
||||
|
||||
rootcommand = '^!storycraft'
|
||||
statuspattern = rootcommand + '\s+status(\s+(\S+)$|$)'
|
||||
newgamepattern = rootcommand + '\s+new\s+game(\s+with\s+config\s+(.*)$|$)'
|
||||
joingamepattern = rootcommand + '\s+join\s+game\s+(\S+)$'
|
||||
listgamespattern = rootcommand + '\s+list\s+games\s+(open|in\s+progress)$'
|
||||
startgamepattern = rootcommand + '\s+start\s+game\s+(\S+)$'
|
||||
addlinepattern = rootcommand + '\s+game\s+(\S+)\s+add\s+line\s+(.*)$'
|
||||
|
||||
self.statusre = re.compile(statuspattern)
|
||||
self.newgamere = re.compile(newgamepattern)
|
||||
self.joingamere = re.compile(joingamepattern)
|
||||
self.listgamesre = re.compile(listgamespattern)
|
||||
self.startgamere = re.compile(startgamepattern)
|
||||
self.addlinere = re.compile(addlinepattern)
|
||||
|
||||
def db_init(self):
|
||||
"""Set up the database tables, if they don't exist."""
|
||||
|
||||
version = self.db_module_registered(self.__class__.__name__)
|
||||
if (version == None):
|
||||
# have to create the database tables
|
||||
db = self.get_db()
|
||||
try:
|
||||
db.execute('''
|
||||
CREATE TABLE storycraft_config (
|
||||
master_channel TEXT NOT NULL,
|
||||
concurrent_games INTEGER NOT NULL,
|
||||
default_round_mode INTEGER NOT NULL,
|
||||
default_game_length INTEGER NOT NULL,
|
||||
default_line_length INTEGER NOT NULL,
|
||||
default_random_method INTEGER NOT NULL,
|
||||
default_lines_per_turn INTEGER NOT NULL
|
||||
)''')
|
||||
db.execute('''
|
||||
INSERT INTO storycraft_config
|
||||
(master_channel, concurrent_games, default_round_mode,
|
||||
default_game_length, default_line_length,
|
||||
default_random_method, default_lines_per_turn)
|
||||
VALUES ('#drbotzo', 10, 1, 20, 140, 1, 2)
|
||||
''')
|
||||
db.execute('''
|
||||
CREATE TABLE storycraft_game (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
round_mode INTEGER NOT NULL,
|
||||
game_length INTEGER NOT NULL,
|
||||
line_length INTEGER NOT NULL,
|
||||
random_method INTEGER NOT NULL,
|
||||
lines_per_turn INTEGER NOT NULL,
|
||||
status TEXT NOT NULL,
|
||||
owner_nick TEXT NOT NULL,
|
||||
owner_userhost TEXT NOT NULL,
|
||||
start_time TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
end_time TEXT DEFAULT NULL
|
||||
)''')
|
||||
db.execute('''
|
||||
CREATE TABLE storycraft_player (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
game_id INTEGER NOT NULL,
|
||||
nick TEXT NOT NULL,
|
||||
userhost TEXT NOT NULL,
|
||||
FOREIGN KEY(game_id) REFERENCES storycraft_game(id)
|
||||
)''')
|
||||
db.execute('''
|
||||
CREATE TABLE storycraft_line (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
game_id INTEGER NOT NULL,
|
||||
player_id INTEGER NOT NULL,
|
||||
line TEXT NOT NULL,
|
||||
time TEXT DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY(game_id) REFERENCES storycraft_game(id)
|
||||
FOREIGN KEY(player_id) REFERENCES storycraft_player(id)
|
||||
)''')
|
||||
|
||||
sql = 'INSERT INTO drbotzo_modules VALUES (?,?)'
|
||||
db.execute(sql, (self.__class__.__name__, 1))
|
||||
db.commit()
|
||||
version = 1
|
||||
except sqlite3.Error as e:
|
||||
db.rollback()
|
||||
print("sqlite error: " + str(e))
|
||||
raise
|
||||
|
||||
def do(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Pass storycraft control commands to the appropriate method based on input."""
|
||||
|
||||
if self.statusre.search(what):
|
||||
return self.storycraft_status(connection, event, nick, userhost, what, admin_unlocked)
|
||||
elif self.newgamere.search(what):
|
||||
return self.storycraft_newgame(connection, event, nick, userhost, what, admin_unlocked)
|
||||
elif self.joingamere.search(what):
|
||||
return self.storycraft_joingame(connection, event, nick, userhost, what, admin_unlocked)
|
||||
elif self.listgamesre.search(what):
|
||||
return self.storycraft_listgames(connection, event, nick, userhost, what, admin_unlocked)
|
||||
elif self.startgamere.search(what):
|
||||
return self.storycraft_startgame(connection, event, nick, userhost, what, admin_unlocked)
|
||||
elif self.addlinere.search(what):
|
||||
return self.storycraft_addline(connection, event, nick, userhost, what, admin_unlocked)
|
||||
|
||||
def storycraft_status(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Print information about the storycraft games, or one specific game."""
|
||||
|
||||
match = self.statusre.search(what)
|
||||
if match:
|
||||
if match.group(2):
|
||||
game_id = int(match.group(2))
|
||||
|
||||
# do the status of one game
|
||||
game = self._get_game_details(game_id)
|
||||
if game:
|
||||
# get game string
|
||||
status_str = self._get_game_summary(game)
|
||||
|
||||
return status_str
|
||||
else:
|
||||
return 'Game #{0:d} does not exist.'.format(game_id)
|
||||
else:
|
||||
# do the general status of all games
|
||||
count_completed = self._get_completed_game_count()
|
||||
count_in_progress = self._get_in_progress_game_count()
|
||||
count_open = self._get_open_game_count()
|
||||
count_free = self._get_free_game_count()
|
||||
|
||||
return 'Storycraft {0:s} - {1:d} games completed, {2:d} in progress, {3:d} open. {4:d} slots free.'.format(__version__, count_completed, count_in_progress, count_open, count_free)
|
||||
|
||||
def storycraft_newgame(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Add a game to the system, which users can join."""
|
||||
|
||||
match = self.newgamere.search(what)
|
||||
if match:
|
||||
# ensure the system isn't at capacity
|
||||
count_free = self._get_free_game_count()
|
||||
if count_free > 0:
|
||||
# get the default settings
|
||||
settings = self._get_storycraft_settings()
|
||||
|
||||
master_channel = settings.master_channel
|
||||
round_mode = settings.default_round_mode
|
||||
game_length = settings.default_game_length
|
||||
line_length = settings.default_line_length
|
||||
random_method = settings.default_random_method
|
||||
lines_per_turn = settings.default_lines_per_turn
|
||||
|
||||
# check for custom options
|
||||
if match.group(2):
|
||||
options = match.group(2)
|
||||
|
||||
# override settings with provided options
|
||||
tuples = options.split(';')
|
||||
for option in tuples:
|
||||
optpair = option.split(',')
|
||||
|
||||
if len(optpair) == 2:
|
||||
if optpair[0] == 'round_mode':
|
||||
round_mode = int(optpair[1])
|
||||
elif optpair[0] == 'game_length':
|
||||
game_length = int(optpair[1])
|
||||
elif optpair[0] == 'line_length':
|
||||
line_length = int(optpair[1])
|
||||
elif optpair[0] == 'random_method':
|
||||
random_method = int(optpair[1])
|
||||
elif optpair[0] == 'lines_per_turn':
|
||||
lines_per_turn = int(optpair[1])
|
||||
|
||||
# add a new game
|
||||
game_id = self._add_new_game(round_mode, game_length, line_length, random_method, lines_per_turn, nick, userhost)
|
||||
if game_id:
|
||||
# add the player to the game, too
|
||||
self._add_player_to_game(game_id, nick, userhost)
|
||||
|
||||
# tell the control channel
|
||||
self.irc.reply(connection, master_channel, '{0:s} created a game of storycraft - do \'!storycraft join game {1:d}\' to take part!'.format(nick, game_id))
|
||||
|
||||
return 'Game #{0:d} has been created. When everyone has joined, do \'!storycraft start game {0:d}\''.format(game_id)
|
||||
else:
|
||||
return 'Error creating game.'
|
||||
else:
|
||||
return 'All slots are full.'
|
||||
|
||||
def storycraft_joingame(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Add a player to an open game."""
|
||||
|
||||
match = self.joingamere.search(what)
|
||||
if match:
|
||||
game_id = int(match.group(1))
|
||||
|
||||
# ensure the game exists
|
||||
game = self._get_game_details(game_id)
|
||||
if game:
|
||||
# check that the game hasn't started yet
|
||||
if game.status == 'OPEN':
|
||||
# see if userhost is already in the game
|
||||
if not self._get_player_exists_in_game(game_id, userhost):
|
||||
# add the player
|
||||
if self._add_player_to_game(game_id, nick, userhost):
|
||||
return '{0:s}, welcome to the game.'.format(nick)
|
||||
else:
|
||||
return 'Error joining game.'
|
||||
else:
|
||||
return 'You are already in game #{0:d}.'.format(game_id)
|
||||
else:
|
||||
return 'Game #{0:d} is not open for new players.'.format(game_id)
|
||||
else:
|
||||
return 'Game #{0:d} does not exist.'.format(game_id)
|
||||
|
||||
def storycraft_listgames(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Get the listing of either open or in progress games."""
|
||||
|
||||
match = self.listgamesre.search(what)
|
||||
if match:
|
||||
category = match.group(1)
|
||||
|
||||
if category == 'open':
|
||||
games = self._get_open_games()
|
||||
|
||||
gamestrs = []
|
||||
for game in games:
|
||||
gamestrs.append(self._get_game_summary(game))
|
||||
|
||||
return '\n'.join(gamestrs)
|
||||
elif category == 'in progress':
|
||||
games = self._get_in_progress_games()
|
||||
|
||||
gamestrs = []
|
||||
for game in games:
|
||||
gamestrs.append(self._get_game_summary(game))
|
||||
|
||||
return '\n'.join(gamestrs)
|
||||
|
||||
def storycraft_startgame(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Start a game, closing the period to join and starting line trading."""
|
||||
|
||||
match = self.startgamere.search(what)
|
||||
if match:
|
||||
game_id = int(match.group(1))
|
||||
|
||||
# retrieve the game
|
||||
game = self._get_game_details(game_id)
|
||||
if game:
|
||||
# check that the user is the owner of the game
|
||||
if userhost == game.owner_userhost:
|
||||
# check that the game is open
|
||||
if 'OPEN' == game.status:
|
||||
# start the game
|
||||
if self._start_game(game_id):
|
||||
# determine the first person to send a line
|
||||
if self._pick_next_player(game_id):
|
||||
# indicate the status
|
||||
game = self._get_game_details(game_id)
|
||||
line = self._get_lines_for_game(game_id)[0]
|
||||
player = self._get_player_by_id(line.player_id)
|
||||
|
||||
# get the default settings
|
||||
settings = self._get_storycraft_settings()
|
||||
master_channel = settings.master_channel
|
||||
|
||||
# tell the control channel
|
||||
self.irc.reply(connection, master_channel, '{0:s} started storycraft #{1:d}! - first player is {2:s}, do \'!storycraft game {1:d} add line YOURTEXT\' when it\'s your turn to reply!'.format(nick, game_id, player.nick))
|
||||
|
||||
return 'Game #{0:d} started.'.format(game_id)
|
||||
else:
|
||||
return 'Failed to assign game to first player.'
|
||||
else:
|
||||
return 'Could not start game.'
|
||||
else:
|
||||
return 'Game #{0:d} is not open.'.format(game_id)
|
||||
else:
|
||||
return 'You are not the owner of #{0:d}.'.format(game_id)
|
||||
else:
|
||||
return 'Game #{0:d} does not exist.'.format(game_id)
|
||||
|
||||
def storycraft_addline(self, connection, event, nick, userhost, what, admin_unlocked):
|
||||
"""Add a line to an in progress game."""
|
||||
|
||||
match = self.addlinere.search(what)
|
||||
if match:
|
||||
game_id = int(match.group(1))
|
||||
input_line = match.group(2)
|
||||
|
||||
# retrieve the game
|
||||
game = self._get_game_details(game_id)
|
||||
if game:
|
||||
# check that the game is open
|
||||
if 'IN PROGRESS' == game.status:
|
||||
# get the most recent line
|
||||
lines = self._get_lines_for_game(game_id)
|
||||
if lines:
|
||||
line = lines[0]
|
||||
|
||||
# check that the line is a prompt for input
|
||||
if line.line == '':
|
||||
player = self._get_player_by_id(line.player_id)
|
||||
if player.userhost == userhost:
|
||||
# check the input line length
|
||||
if len(input_line) <= game.line_length:
|
||||
# add line
|
||||
if self._update_line(line.id, input_line):
|
||||
# determine the first person to send a line
|
||||
if self._pick_next_player(game_id):
|
||||
# indicate the status
|
||||
game = self._get_game_details(game_id)
|
||||
line = self._get_lines_for_game(game_id)[0]
|
||||
player = self._get_player_by_id(line.player_id)
|
||||
|
||||
# get the default settings
|
||||
settings = self._get_storycraft_settings()
|
||||
master_channel = settings.master_channel
|
||||
|
||||
# log output
|
||||
if game.status == 'IN PROGRESS':
|
||||
self.irc.reply(connection, master_channel, '{0:s} added a line to storycraft #{1:d}! - next player is {2:s}'.format(nick, game_id, player.nick))
|
||||
return 'Line logged.'
|
||||
else:
|
||||
self.irc.reply(connection, master_channel, '{0:s} finished storycraft #{1:d}!'.format(nick, game_id))
|
||||
return 'Line logged (and game completed).'
|
||||
else:
|
||||
return 'Failed to assign game to next player.'
|
||||
else:
|
||||
return 'Failed to save your line.'
|
||||
else:
|
||||
return 'The maximum line length for this game is {0:d} characters.'.format(game.line_length)
|
||||
else:
|
||||
return 'You are not the assignee of the current game.'
|
||||
else:
|
||||
return 'Game is in progress but the most recent line is not available - internal consistency error.'
|
||||
else:
|
||||
return 'Game #{0:d} has no lines - internal consistency error.'.format(game_id)
|
||||
else:
|
||||
return 'Game #{0:d} has not been started.'.format(game_id)
|
||||
else:
|
||||
return 'Game #{0:d} does not exist.'.format(game_id)
|
||||
|
||||
def _get_game_details(self, game_id):
|
||||
"""Get the details of one game."""
|
||||
|
||||
try:
|
||||
# get the specified game and populate a StorycraftGame
|
||||
db = self.get_db()
|
||||
query = '''
|
||||
SELECT id, round_mode, game_length, line_length, random_method,
|
||||
lines_per_turn, status, owner_nick, owner_userhost, start_time,
|
||||
end_time
|
||||
FROM storycraft_game WHERE id = ?
|
||||
'''
|
||||
cursor = db.execute(query, (game_id,))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
game = self.StorycraftGame()
|
||||
|
||||
game.id = int(result['id'])
|
||||
game.round_mode = int(result['round_mode'])
|
||||
game.game_length = int(result['game_length'])
|
||||
game.line_length = int(result['line_length'])
|
||||
game.random_method = int(result['random_method'])
|
||||
game.lines_per_turn = int(result['lines_per_turn'])
|
||||
game.status = result['status']
|
||||
game.owner_nick = result['owner_nick']
|
||||
game.owner_userhost = result['owner_userhost']
|
||||
game.start_time = result['start_time']
|
||||
game.end_time = result['end_time']
|
||||
|
||||
return game
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_game_summary(self, game):
|
||||
"""Display game info for a general summary."""
|
||||
|
||||
status_str = '#{0:d} - created on {1:s} by {2:s}, {3:s}'.format(game.id, game.start_time, game.owner_nick, game.status)
|
||||
if game.status == 'COMPLETED' and game.end_time:
|
||||
status_str = status_str + ' ({0:s})'.format(game.end_time)
|
||||
elif game.status == 'IN PROGRESS':
|
||||
line = self._get_lines_for_game(game.id)[0]
|
||||
player = self._get_player_by_id(line.player_id)
|
||||
status_str = status_str + ' (next is {0:s})'.format(player.nick)
|
||||
|
||||
# get players in game
|
||||
player_names = []
|
||||
players = self._get_player_list_for_game(game.id)
|
||||
if players:
|
||||
for player in players:
|
||||
player_names.append(player.nick)
|
||||
status_str = status_str + ', players: {0:s}'.format(', '.join(player_names))
|
||||
|
||||
status_str = status_str + '. (o:{0:d}[{1:d}],{2:d},{3:d},{4:d})'.format(game.round_mode, game.game_length, game.line_length, game.random_method, game.lines_per_turn)
|
||||
|
||||
return status_str
|
||||
|
||||
def _add_new_game(self, round_mode, game_length, line_length, random_method, lines_per_turn, nick, userhost):
|
||||
"""Add a new game to the system."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
INSERT INTO storycraft_game (
|
||||
round_mode, game_length, line_length, random_method,
|
||||
lines_per_turn, status, owner_nick, owner_userhost
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
'''
|
||||
cur.execute(statement, (round_mode, game_length, line_length, random_method, lines_per_turn, 'OPEN', nick, userhost))
|
||||
db.commit()
|
||||
return cur.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_player_list_for_game(self, game_id):
|
||||
"""Get the list of players in one game."""
|
||||
|
||||
players = []
|
||||
try:
|
||||
# get the players for specified game and populate a list
|
||||
db = self.get_db()
|
||||
query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = ?'
|
||||
cursor = db.execute(query, (game_id,))
|
||||
results = cursor.fetchall()
|
||||
for result in results:
|
||||
player = self.StorycraftPlayer()
|
||||
|
||||
player.id = result['id']
|
||||
player.game_id = result['game_id']
|
||||
player.nick = result['nick']
|
||||
player.userhost = result['userhost']
|
||||
|
||||
players.append(player)
|
||||
|
||||
return players
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_game_exists(self, game_id):
|
||||
"""Return the existence of a game."""
|
||||
|
||||
return self._get_game_details(game_id) is not None
|
||||
|
||||
def _start_game(self, game_id):
|
||||
"""Start a game, if it's currently open."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
UPDATE storycraft_game SET status = 'IN PROGRESS' WHERE status = 'OPEN' AND id = ?
|
||||
'''
|
||||
cur.execute(statement, (game_id,))
|
||||
db.commit()
|
||||
return game_id
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _pick_next_player(self, game_id):
|
||||
"""Based on the game's settings and state, set who will be replying next."""
|
||||
|
||||
game = self._get_game_details(game_id)
|
||||
if game:
|
||||
# do round_mode stuff
|
||||
if game.round_mode == 1:
|
||||
count_by_latest_player = 0
|
||||
latest_player_id = 0
|
||||
|
||||
# get the lines in this game, latest first
|
||||
lines = self._get_lines_for_game(game_id)
|
||||
|
||||
for line in lines:
|
||||
if latest_player_id == 0:
|
||||
latest_player_id = line.player_id
|
||||
|
||||
if latest_player_id == line.player_id:
|
||||
count_by_latest_player = count_by_latest_player + 1
|
||||
else:
|
||||
break
|
||||
|
||||
# now we know how many times the most recent person has responded,
|
||||
# figure out what to do
|
||||
if count_by_latest_player == 0:
|
||||
# must be a new game, get a random player
|
||||
players = self._get_player_list_for_game(game.id)
|
||||
random_player = players[random.randint(1,len(players))-1]
|
||||
return self._assign_game_to_player(game_id, random_player.id)
|
||||
elif count_by_latest_player >= game.lines_per_turn and len(lines) < game.game_length:
|
||||
# player has reached the max, get a random player to assign
|
||||
players = self._get_player_list_for_game(game.id)
|
||||
random_player = players[random.randint(1,len(players))-1]
|
||||
return self._assign_game_to_player(game_id, random_player.id)
|
||||
elif count_by_latest_player >= game.lines_per_turn and len(lines) >= game.game_length:
|
||||
# end of game
|
||||
return self._end_game(game_id)
|
||||
elif count_by_latest_player < game.lines_per_turn:
|
||||
# player should continue getting lines
|
||||
return self._assign_game_to_player(game_id, latest_player_id)
|
||||
|
||||
def _assign_game_to_player(self, game_id, player_id):
|
||||
"""Assign the game to a player, prompting them for responses."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
INSERT INTO storycraft_line (game_id, player_id, line)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
cur.execute(statement, (game_id, player_id, ''))
|
||||
db.commit()
|
||||
return cur.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _end_game(self, game_id):
|
||||
"""End the given game, disallowing adding lines."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
UPDATE storycraft_game SET status = 'COMPLETED', end_time = CURRENT_TIMESTAMP
|
||||
WHERE status = 'IN PROGRESS' AND id = ?
|
||||
'''
|
||||
cur.execute(statement, (game_id,))
|
||||
db.commit()
|
||||
return game_id
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_player_by_id(self, player_id):
|
||||
"""Get the player details based on id."""
|
||||
|
||||
try:
|
||||
# get the specified player and populate a StorycraftPlayer
|
||||
db = self.get_db()
|
||||
query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE id = ?'
|
||||
cursor = db.execute(query, (player_id,))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
player = self.StorycraftPlayer()
|
||||
|
||||
player.id = result['id']
|
||||
player.game_id = result['game_id']
|
||||
player.nick = result['nick']
|
||||
player.userhost = result['userhost']
|
||||
|
||||
return player
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_player_by_userhost_in_game(self, game_id, userhost):
|
||||
"""Get the player details if they exist in the given game."""
|
||||
|
||||
try:
|
||||
# get the specified player if they exist in the game and populate a StorycraftPlayer
|
||||
db = self.get_db()
|
||||
query = 'SELECT id, game_id, nick, userhost FROM storycraft_player WHERE game_id = ? AND userhost = ?'
|
||||
cursor = db.execute(query, (game_id,userhost))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
player = self.StorycraftPlayer()
|
||||
|
||||
player.id = result['id']
|
||||
player.game_id = result['game_id']
|
||||
player.nick = result['nick']
|
||||
player.userhost = result['userhost']
|
||||
|
||||
return player
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_completed_games(self):
|
||||
"""Get the games with COMPLETED status."""
|
||||
|
||||
return self._get_games_of_type('COMPLETED')
|
||||
|
||||
def _get_in_progress_games(self):
|
||||
"""Get the games with IN PROGRESS status."""
|
||||
|
||||
return self._get_games_of_type('IN PROGRESS')
|
||||
|
||||
def _get_open_games(self):
|
||||
"""Get the games with OPEN status."""
|
||||
|
||||
return self._get_games_of_type('OPEN')
|
||||
|
||||
def _get_games_of_type(self, game_type):
|
||||
"""Return the games of the specified type."""
|
||||
|
||||
games = []
|
||||
try:
|
||||
# get the games of specified type and populate a list
|
||||
db = self.get_db()
|
||||
query = '''
|
||||
SELECT id, round_mode, game_length, line_length, random_method,
|
||||
lines_per_turn, status, owner_nick, owner_userhost, start_time,
|
||||
end_time
|
||||
FROM storycraft_game WHERE status = ?
|
||||
'''
|
||||
cursor = db.execute(query, (game_type,))
|
||||
results = cursor.fetchall()
|
||||
for result in results:
|
||||
game = self.StorycraftGame()
|
||||
|
||||
game.id = int(result['id'])
|
||||
game.round_mode = int(result['round_mode'])
|
||||
game.game_length = int(result['game_length'])
|
||||
game.line_length = int(result['line_length'])
|
||||
game.random_method = int(result['random_method'])
|
||||
game.lines_per_turn = int(result['lines_per_turn'])
|
||||
game.status = result['status']
|
||||
game.owner_nick = result['owner_nick']
|
||||
game.owner_userhost = result['owner_userhost']
|
||||
game.start_time = result['start_time']
|
||||
game.end_time = result['end_time']
|
||||
|
||||
games.append(game)
|
||||
|
||||
return games
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _add_player_to_game(self, game_id, nick, userhost):
|
||||
"""Add a player to a game, so that they may eventually play."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
INSERT INTO storycraft_player (game_id, nick, userhost)
|
||||
VALUES (?, ?, ?)
|
||||
'''
|
||||
cur.execute(statement, (game_id, nick, userhost))
|
||||
db.commit()
|
||||
return cur.lastrowid
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_player_exists_in_game(self, game_id, userhost):
|
||||
"""Return the existence of a player in a game."""
|
||||
|
||||
return self._get_player_by_userhost_in_game(game_id, userhost) is not None
|
||||
|
||||
def _get_completed_game_count(self):
|
||||
"""Get the number of games with COMPLETED status."""
|
||||
|
||||
return self._get_game_type_count('COMPLETED')
|
||||
|
||||
def _get_in_progress_game_count(self):
|
||||
"""Get the number of games with IN PROGRESS status."""
|
||||
|
||||
return self._get_game_type_count('IN PROGRESS')
|
||||
|
||||
def _get_open_game_count(self):
|
||||
"""Get the number of games with OPEN status."""
|
||||
|
||||
return self._get_game_type_count('OPEN')
|
||||
|
||||
def _get_free_game_count(self):
|
||||
"""Get the number of slots available, given current in progess/open games."""
|
||||
|
||||
all_slots = self._get_concurrent_game_count()
|
||||
count_in_progress = self._get_in_progress_game_count()
|
||||
count_open = self._get_open_game_count()
|
||||
return all_slots - count_in_progress - count_open
|
||||
|
||||
def _get_game_type_count(self, game_type):
|
||||
"""Return the number of games of the specified type."""
|
||||
|
||||
count = 0
|
||||
try:
|
||||
# get count of game_type games
|
||||
db = self.get_db()
|
||||
query = 'SELECT COUNT(*) FROM storycraft_game WHERE status = ?'
|
||||
cursor = db.execute(query, (game_type,))
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
count = result['COUNT(*)']
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
return count
|
||||
|
||||
def _get_concurrent_game_count(self):
|
||||
"""Return the current game server concurrency."""
|
||||
|
||||
concurrency = 0
|
||||
try:
|
||||
# get the concurrency value from config table
|
||||
db = self.get_db()
|
||||
query = 'SELECT concurrent_games FROM storycraft_config'
|
||||
cursor = db.execute(query)
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
concurrency = result['concurrent_games']
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
return concurrency
|
||||
|
||||
def _get_lines_for_game(self, game_id):
|
||||
"""Get the lines for the specified game_id."""
|
||||
|
||||
lines = []
|
||||
try:
|
||||
# get the games of specified type and populate a list
|
||||
db = self.get_db()
|
||||
query = '''
|
||||
SELECT id, game_id, player_id, line, time
|
||||
FROM storycraft_line WHERE game_id = ?
|
||||
ORDER BY time DESC
|
||||
'''
|
||||
cursor = db.execute(query, (game_id,))
|
||||
results = cursor.fetchall()
|
||||
for result in results:
|
||||
line = self.StorycraftLine()
|
||||
|
||||
line.id = result['id']
|
||||
line.game_id = result['game_id']
|
||||
line.player_id = result['player_id']
|
||||
line.line = result['line']
|
||||
line.time = result['time']
|
||||
|
||||
lines.append(line)
|
||||
|
||||
return lines
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _update_line(self, line_id, input_line):
|
||||
"""Update the specified line with the given text."""
|
||||
|
||||
try:
|
||||
db = self.get_db()
|
||||
cur = db.cursor()
|
||||
statement = '''
|
||||
UPDATE storycraft_line SET line = ?, time = CURRENT_TIMESTAMP WHERE id = ?
|
||||
'''
|
||||
cur.execute(statement, (line_id, input_line))
|
||||
db.commit()
|
||||
return line_id
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
def _get_storycraft_settings(self):
|
||||
"""Get the server settings."""
|
||||
|
||||
try:
|
||||
# get the settings and return in StorycraftSettings
|
||||
db = self.get_db()
|
||||
query = '''
|
||||
SELECT master_channel, concurrent_games, default_round_mode,
|
||||
default_game_length, default_line_length, default_random_method,
|
||||
default_lines_per_turn
|
||||
FROM storycraft_config
|
||||
'''
|
||||
cursor = db.execute(query)
|
||||
result = cursor.fetchone()
|
||||
if result:
|
||||
settings = self.StorycraftSettings()
|
||||
|
||||
settings.master_channel = result['master_channel']
|
||||
settings.concurrent_games = int(result['concurrent_games'])
|
||||
settings.default_round_mode = int(result['default_round_mode'])
|
||||
settings.default_game_length = int(result['default_game_length'])
|
||||
settings.default_line_length = int(result['default_line_length'])
|
||||
settings.default_random_method = int(result['default_random_method'])
|
||||
settings.default_lines_per_turn = int(result['default_lines_per_turn'])
|
||||
|
||||
return settings
|
||||
except sqlite3.Error as e:
|
||||
print('sqlite error: ' + str(e))
|
||||
raise
|
||||
|
||||
# vi:tabstop=4:expandtab:autoindent
|
Loading…
Reference in New Issue