parent
107d9fe264
commit
85d305ee06
@ -0,0 +1,140 @@ |
||||
import random |
||||
import string |
||||
|
||||
from django.db import models, transaction |
||||
from django.utils import timezone |
||||
|
||||
|
||||
def generate_code(length): |
||||
return "".join([random.choice(string.ascii_lowercase) |
||||
for i in range(length)]) |
||||
|
||||
|
||||
class Game(models.Model): |
||||
ACCESS_CODE_LENGTH = 6 |
||||
access_code = models.CharField(db_index=True, unique=True, |
||||
max_length=ACCESS_CODE_LENGTH) |
||||
GAME_PHASE_LOBBY = 0 |
||||
GAME_PHASE_PROPOSE = 1 |
||||
GAME_PHASE_CLUE = 2 |
||||
GAME_PHASE_SOLVE = 3 |
||||
GAME_PHASE_END = 4 |
||||
game_phase = models.IntegerField(default=GAME_PHASE_LOBBY) |
||||
default_cards_per_player = models.IntegerField() |
||||
num_initial_free_clues = models.IntegerField() |
||||
num_unlock_clues_per_player = models.IntegerField() |
||||
num_unlockable_clues = models.IntegerField() |
||||
remaining_deck_order = models.CharField(max_length=64) |
||||
created = models.DateTimeField() |
||||
ended = models.DateTimeField(null=True, default=None) |
||||
next_game = models.OneToOneField('self', null=True, default=None, |
||||
related_name='previous_game') |
||||
|
||||
# from http://stackoverflow.com/a/11821832 |
||||
def save(self, *args, **kwargs): |
||||
# object is being created, thus no primary key field yet |
||||
if not self.pk: |
||||
# Make sure access_code is unique before using it. |
||||
access_code = generate_code(Game.ACCESS_CODE_LENGTH) |
||||
while Game.objects.filter(access_code=access_code).exists(): |
||||
access_code = generate_code(Game.ACCESS_CODE_LENGTH) |
||||
self.access_code = access_code |
||||
self.created = timezone.now() |
||||
if self.ended is None and self.game_phase == Game.GAME_PHASE_END: |
||||
self.ended = timezone.now() |
||||
super(Game, self).save(*args, **kwargs) |
||||
|
||||
_game_phase_strings = { |
||||
GAME_PHASE_LOBBY: 'lobby', |
||||
GAME_PHASE_PROPOSE: 'propose', |
||||
GAME_PHASE_CLUE: 'clue', |
||||
GAME_PHASE_SOLVE: 'solve', |
||||
GAME_PHASE_END: 'end', |
||||
} |
||||
|
||||
def game_phase_string(self): |
||||
return Game._game_phase_strings[self.game_phase] |
||||
|
||||
def num_players(self): |
||||
return self.player_set.filter(is_player=True).count() |
||||
|
||||
def num_non_player_stands(self): |
||||
return self.player_set.filter(is_player=False).count() |
||||
|
||||
@transaction.atomic |
||||
def create_or_get_next_game(self): |
||||
if self.next_game is None and self.game_phase == self.GAME_PHASE_END: |
||||
self.next_game = Game.objects.create() |
||||
self.save() |
||||
return self.next_game |
||||
|
||||
|
||||
class Player(models.Model): |
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True) |
||||
SECRET_ID_LENGTH = 8 |
||||
secret_id = models.CharField(db_index=True, max_length=SECRET_ID_LENGTH) |
||||
name = models.CharField(max_length=80) |
||||
player_num = models.IntegerField() |
||||
is_player = models.BooleanField() # as opposed to non-player stack |
||||
ready = models.BooleanField(default=False) |
||||
# num_cards is redundant with cards, but used during setup. |
||||
num_cards = models.IntegerField() |
||||
cards = models.CharField(max_length=16) |
||||
ready = models.BooleanField(default=False) |
||||
joined = models.DateTimeField() |
||||
last_accessed = models.DateTimeField() |
||||
# names are unique in a game |
||||
unique_together = (("game", "name"), ("game", "secret_id")) |
||||
|
||||
|
||||
class Turn(models.Model): |
||||
game = models.ForeignKey(Game, on_delete=models.CASCADE, db_index=True) |
||||
turn_num = models.IntegerField() |
||||
visible_cards = models.CharField(max_length=64) |
||||
clue = models.OneToOneField('ClueProposal', null=True) |
||||
|
||||
|
||||
class ClueProposal(models.Model): |
||||
turn = models.ForeignKey(Turn, on_delete=models.CASCADE, db_index=True) |
||||
player = models.ForeignKey(Player, on_delete=models.CASCADE, db_index=True) |
||||
card_indexes = models.CharField(max_length=80) |
||||
|
||||
|
||||
class ClueProposalVote(models.Model): |
||||
turn = models.ForeignKey(Turn, on_delete=models.CASCADE, db_index=True) |
||||
player = models.ForeignKey(Player, |
||||
on_delete=models.CASCADE, |
||||
db_index=True) |
||||
clue_proposal = models.OneToOneField(ClueProposal) |
||||
|
||||
|
||||
class AdvanceDecision(models.Model): |
||||
turn = models.ForeignKey(Turn, on_delete=models.CASCADE, db_index=True) |
||||
player = models.ForeignKey(Player, |
||||
on_delete=models.CASCADE, |
||||
db_index=True) |
||||
advance_card = models.BooleanField(null=True) |
||||
guess = models.CharField(null=True, max_length=1) |
||||
|
||||
|
||||
class BonusCardGuess(models.Model): |
||||
turn = models.ForeignKey(Turn, on_delete=models.CASCADE, db_index=True) |
||||
player = models.ForeignKey(Player, |
||||
on_delete=models.CASCADE, |
||||
db_index=True) |
||||
guess = models.CharField(null=True, max_length=1) |
||||
|
||||
|
||||
class Notes(models.Model): |
||||
turn = models.ForeignKey(Turn, on_delete=models.CASCADE, db_index=True) |
||||
player = models.ForeignKey(Player, |
||||
on_delete=models.CASCADE, |
||||
db_index=True) |
||||
notes = models.CharField(max_length=80) |
||||
|
||||
|
||||
class Solve(models.Model): |
||||
player = models.ForeignKey(Player, |
||||
on_delete=models.CASCADE, |
||||
db_index=True) |
||||
card_indexes = models.CharField(max_length=80) |
@ -0,0 +1,33 @@ |
||||
Also provide a way to take notes for physical game? Possibly entirely separate |
||||
code? Hopefully not... |
||||
|
||||
Model: |
||||
|
||||
Game: |
||||
* # of players |
||||
* Card orders? |
||||
* # of clues left? |
||||
|
||||
Player names? |
||||
|
||||
Turn: |
||||
* Visible cards at turn |
||||
|
||||
Players intention to advance and bonus letter guesses? |
||||
|
||||
Clue proposals: |
||||
* Player # giving clue. |
||||
* Turn # (to know what cards are visible) |
||||
* List of indexes into visible cards |
||||
* Should this just be a string like "423*"? Slightly complicated by bonus |
||||
letters... but using A-Za-z for bonus letters and not allowing more |
||||
than 52 bonus letter should be fine: the deck is only 64 cards anyway. |
||||
|
||||
Votes on clue proposals? Separate table? Or part of a clue proposal? |
||||
|
||||
Clues: |
||||
* Clue proposal actuall used. |
||||
|
||||
Player notes? |
||||
|
||||
Final answer? |
Loading…
Reference in new issue