Bunco Simulator

Check-in [f25ad4]
Overview
Comment:Log dice rolls and scores to a database
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: f25ad40794bd598c6d7802b4d2f9b0a3af801ce348ec78ffc972b0b3ce2b68d9
User & Date: joel on 2018-11-21 05:15:46
Other Links: manifest | tags
Context
2018-11-21
20:54
Log tables moves by players in between rounds check-in: 9538c9 user: joel tags: trunk
05:15
Log dice rolls and scores to a database check-in: f25ad4 user: joel tags: trunk
01:34
Track current round in the Player class; various streamlinings check-in: 81a7b3 user: joel tags: trunk
Changes

Modified bunco.py from [929d7a] to [94a406].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# Test
import csv, random

teammate_lookup = { 0: 2, 1: 3, 2: 0, 3: 1 }

def TurnInProgress():
    return -1

def roll_dice():
    dice = []
    for _ in range(3):
        dice.append(random.randint(1,6))
    return dice

class Player:
    fuzzydie_holder = None
    current_round = 1

    def __init__(self, name, dex, math, speed):
        self.name = name
        self.dex = dex
        self.math_comprehension = math
        self.roll_speed = speed
        self.max_streak = 0

|














<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

17
18
19
20
21
22
23
# Test
import csv, random, sqlite3

teammate_lookup = { 0: 2, 1: 3, 2: 0, 3: 1 }

def TurnInProgress():
    return -1

def roll_dice():
    dice = []
    for _ in range(3):
        dice.append(random.randint(1,6))
    return dice

class Player:
    fuzzydie_holder = None


    def __init__(self, name, dex, math, speed):
        self.name = name
        self.dex = dex
        self.math_comprehension = math
        self.roll_speed = speed
        self.max_streak = 0
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74

75
76
77
78
79
80
81
        self.round_scores.append(0)
        self.round_roll_counts.append(0)
        self.personal_roll_scores.append(0)
        self.turn_progress = 0
        self.current_streak = 0

    def score_last_roll(self):
        desired_num = self.current_round % 6
        desired_num = 6 if desired_num == 0 else desired_num
    
        roll_score = 0
        
        if all(die == desired_num for die in self.current_roll):
            # Bunco!
            roll_score = 21
        elif all(die == self.current_roll[0] for die in self.current_roll):
            # All three dice match, but not Bunco
            roll_score = 5
        else:
            for die in self.current_roll:
                roll_score += 1 if die == desired_num else 0

        if roll_score > 0:
            self.current_streak += 1
            self.max_streak = max(self.current_streak, self.max_streak)
            self.round_scores[self.current_round - 1] += roll_score
            self.personal_roll_scores[self.current_round - 1] += roll_score
            if roll_score == 21:
                self.bunco_count += 1
                self.rolled_bunco = True
        else:
            self.current_streak = 0
        

        return roll_score

    def tick(self):
        result = TurnInProgress()

        if self.fuzzydie_holder is self:
            self.current_fuzzydie_streak += 1







|

















|
|






>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
        self.round_scores.append(0)
        self.round_roll_counts.append(0)
        self.personal_roll_scores.append(0)
        self.turn_progress = 0
        self.current_streak = 0

    def score_last_roll(self):
        desired_num = Game.current_round() % 6
        desired_num = 6 if desired_num == 0 else desired_num
    
        roll_score = 0
        
        if all(die == desired_num for die in self.current_roll):
            # Bunco!
            roll_score = 21
        elif all(die == self.current_roll[0] for die in self.current_roll):
            # All three dice match, but not Bunco
            roll_score = 5
        else:
            for die in self.current_roll:
                roll_score += 1 if die == desired_num else 0

        if roll_score > 0:
            self.current_streak += 1
            self.max_streak = max(self.current_streak, self.max_streak)
            self.round_scores[Game.current_round() - 1] += roll_score
            self.personal_roll_scores[Game.current_round() - 1] += roll_score
            if roll_score == 21:
                self.bunco_count += 1
                self.rolled_bunco = True
        else:
            self.current_streak = 0
        
        log_roll(self, self.current_roll, roll_score)
        return roll_score

    def tick(self):
        result = TurnInProgress()

        if self.fuzzydie_holder is self:
            self.current_fuzzydie_streak += 1
92
93
94
95
96
97
98
99

100
101
102
103
104
105
106
            # TODO: Incorporate ROLL SPEED stat
            self.turn_progress += random.randint(12,25)
        elif self.turn_progress < 75:
            # Reading the numbers
            # TODO: Incorporate MATH COMPREHENSION stat
            if not self.current_roll:
                self.current_roll = roll_dice()
                self.round_roll_counts[self.current_round - 1] += 1

            self.turn_progress += random.randint(12,25)
        else:
            # Finished reading the numbers -- good job, eyeballs and brain!
            result = self.score_last_roll()
            self.current_roll = []
            self.turn_progress = 0
        return result







|
>







92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
            # TODO: Incorporate ROLL SPEED stat
            self.turn_progress += random.randint(12,25)
        elif self.turn_progress < 75:
            # Reading the numbers
            # TODO: Incorporate MATH COMPREHENSION stat
            if not self.current_roll:
                self.current_roll = roll_dice()
                self.round_roll_counts[Game.current_round() - 1] += 1
                log(self, f"{self.name} rolled the dice")
            self.turn_progress += random.randint(12,25)
        else:
            # Finished reading the numbers -- good job, eyeballs and brain!
            result = self.score_last_roll()
            self.current_roll = []
            self.turn_progress = 0
        return result
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132

    def __repr__(self):
        names = ", ".join([p.name for p in self.players])
        return f"<Table {self.table_number}: {names}>"

    def update_teammate_score(self, score):
        teammate = self.players[teammate_lookup[self.active_player]]
        teammate.round_scores[Player.current_round - 1] += score

    def tick(self):
        if self.active_player == -1:
            # First tick for the table this round
            self.active_player = random.randint(1, len(self.players)) - 1

        result = self.players[self.active_player].tick()







|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133

    def __repr__(self):
        names = ", ".join([p.name for p in self.players])
        return f"<Table {self.table_number}: {names}>"

    def update_teammate_score(self, score):
        teammate = self.players[teammate_lookup[self.active_player]]
        teammate.round_scores[Game.current_round() - 1] += score

    def tick(self):
        if self.active_player == -1:
            # First tick for the table this round
            self.active_player = random.randint(1, len(self.players)) - 1

        result = self.players[self.active_player].tick()
147
148
149
150
151
152
153

154
155
156

157
158
159
160
161
162
163
    def roll_off(self):
        """Attempt to settle a tie by going round the table once"""
        self.active_player = 0
        
        # Let the first player go until they roll for 0 points
        while self.active_player == 0:
            self.tick()

        # Now let the rest do the same
        while self.active_player != 0:
            self.tick()


    def losers(self):
        if self.team1_score > self.team2_score:
            return self.players[1::2]
        else:
            return self.players[0::2]
    







>



>







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
    def roll_off(self):
        """Attempt to settle a tie by going round the table once"""
        self.active_player = 0
        
        # Let the first player go until they roll for 0 points
        while self.active_player == 0:
            self.tick()
            Game.increment_tick()
        # Now let the rest do the same
        while self.active_player != 0:
            self.tick()
            Game.increment_tick()

    def losers(self):
        if self.team1_score > self.team2_score:
            return self.players[1::2]
        else:
            return self.players[0::2]
    
231
232
233
234
235
236
237



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256


















257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291

292
293
294
295
296
297
298
299





















        tsvreader = csv.reader(tsvfile)
        for line in tsvreader:
            players.append(Player(line[0],0,0,0))
    
    return players

class Game:



    def __init__(self, playerfile):
        self.players = load_players(playerfile)
        self.tables = assign_teams(self.players)
        Player.current_round = 1

    def tick(self):
        for table in self.tables:
            table.tick()
        
        bunco_rollers = [p for p in self.players if p.rolled_bunco is True]
        
        # If multiple people rolled Bunco this tick, and one of them already has the
        # fuzzy die, they retain it.
        # Otherwise, the last person in the list gets the fuzzy die.
        if bunco_rollers and Player.fuzzydie_holder not in bunco_rollers:
            Player.fuzzydie_holder = bunco_rollers[-1]
            for player in bunco_rollers:
                player.rolled_bunco = False # Reset flag



















    def print_status(self):
        for n, table in enumerate(self.tables):
            print(f"== TABLE {n+1} == Team 1:{table.team1_score} pts, Team 2:{table.team2_score} pts")
            for player in table.players:
                print(f"    {player.name} {player.round_scores[Player.current_round - 1]} points, streak {player.max_streak} buncos {player.bunco_count}")

    def prep_next_round(self):
        # losers from head table move to next table
        headtable_losers = self.tables[0].losers()
        round_winners = self.tables[1].swap_for_winners(headtable_losers)

        # winners from other tables move to next table
        for n in range(2, len(self.tables)):
            round_winners = self.tables[n].swap_for_winners(round_winners)
        # last set of winners moves to head table
        self.tables[0].swap_for_losers(round_winners)

        for table in self.tables:
            table.prep_new_round()

        Player.current_round += 1

    def play_one_round(self):
        # Go until one of the head table teams reaches 21 pts
        while max(self.tables[0].team1_score, self.tables[0].team2_score) < 21:
            self.tick()

        print("\n\n\t\tB U N C O !!!\n\n")
        
        # Finish up scoring for any players that have unscored rolls
        for player in self.players:
            if player.current_roll:
                print(f"Finishing up: {player}")
                while player.current_roll:
                    player.tick()

        
        # Settle ties at each table by doing a roll-off as many times as needed
        for table in self.tables:
            while table.team1_score == table.team2_score:
                print(f"Roll off! {table}")
                table.roll_off()

        self.print_status()




























>
>
>



<















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|















|














>








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
234
235
236
237
238
239
240
241
242
243
244
245
246

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
        tsvreader = csv.reader(tsvfile)
        for line in tsvreader:
            players.append(Player(line[0],0,0,0))
    
    return players

class Game:
    cur_tick = 1
    cur_round = 1
    
    def __init__(self, playerfile):
        self.players = load_players(playerfile)
        self.tables = assign_teams(self.players)


    def tick(self):
        for table in self.tables:
            table.tick()
        
        bunco_rollers = [p for p in self.players if p.rolled_bunco is True]
        
        # If multiple people rolled Bunco this tick, and one of them already has the
        # fuzzy die, they retain it.
        # Otherwise, the last person in the list gets the fuzzy die.
        if bunco_rollers and Player.fuzzydie_holder not in bunco_rollers:
            Player.fuzzydie_holder = bunco_rollers[-1]
            for player in bunco_rollers:
                player.rolled_bunco = False # Reset flag

        self.increment_tick()
    
    @classmethod
    def current_tick(cls):
        return cls.cur_tick
    
    @classmethod
    def increment_tick(cls):
        cls.cur_tick += 1

    @classmethod
    def current_round(cls):
        return cls.cur_round

    @classmethod
    def increment_round(cls):
        cls.cur_round += 1

    def print_status(self):
        for n, table in enumerate(self.tables):
            print(f"== TABLE {n+1} == Team 1:{table.team1_score} pts, Team 2:{table.team2_score} pts")
            for player in table.players:
                print(f"    {player.name} {player.round_scores[Game.current_round() - 1]} points, streak {player.max_streak} buncos {player.bunco_count}")

    def prep_next_round(self):
        # losers from head table move to next table
        headtable_losers = self.tables[0].losers()
        round_winners = self.tables[1].swap_for_winners(headtable_losers)

        # winners from other tables move to next table
        for n in range(2, len(self.tables)):
            round_winners = self.tables[n].swap_for_winners(round_winners)
        # last set of winners moves to head table
        self.tables[0].swap_for_losers(round_winners)

        for table in self.tables:
            table.prep_new_round()

        self.increment_round()

    def play_one_round(self):
        # Go until one of the head table teams reaches 21 pts
        while max(self.tables[0].team1_score, self.tables[0].team2_score) < 21:
            self.tick()

        print("\n\n\t\tB U N C O !!!\n\n")
        
        # Finish up scoring for any players that have unscored rolls
        for player in self.players:
            if player.current_roll:
                print(f"Finishing up: {player}")
                while player.current_roll:
                    player.tick()
                    self.increment_tick()
        
        # Settle ties at each table by doing a roll-off as many times as needed
        for table in self.tables:
            while table.team1_score == table.team2_score:
                print(f"Roll off! {table}")
                table.roll_off()

        self.print_status()
    
log_db = sqlite3.connect("bunco.sqlite")
log_dbc = log_db.cursor()

def run_query(*args):
    log_dbc.execute(*args)
    log_db.commit()

run_query('DROP TABLE IF EXISTS `bunco_log`')
run_query('CREATE TABLE `bunco_log` (id, tick_number, round, player_name, type, message)')

def log_roll(player, dice, score):
    msg = f"{player.name} comprehended that [their] roll of {dice} = {score} points"
    query = """INSERT INTO bunco_log(tick_number, round, player_name, type, message) 
                VALUES(?, ?, ?, ?, ?)"""
    run_query(query, (Game.current_tick(), Game.current_round(), player.name, 'roll', msg))

def log(player, message):
    query = """INSERT INTO bunco_log(tick_number, round, player_name, type, message) 
                VALUES(?, ?, ?, ?, ?)"""
    run_query(query, (Game.current_tick(), Game.current_round(), player.name, 'general', message))