-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathTileDeck.py
More file actions
197 lines (141 loc) · 5.32 KB
/
TileDeck.py
File metadata and controls
197 lines (141 loc) · 5.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
########################################################################
# Threes! is a game by by Asher Vollmer, Greg Wohlwend, Jimmy Hinson, #
# and Hidden Variable. This is created so that AI/ML strategies for #
# the game can be developed and tested easier, and is not intended as #
# a replacement of the original game. Please support the developers by #
# purchasing their excellent game! #
########################################################################
""" Tile Deck
This is the tile deck used by Threes to the best of my knowledge.
"""
###########
# Imports #
###########
# Future imports must occur at the beginning of a file
from __future__ import print_function
import random
######################
# Important Constant #
######################
_BASE = (
1, 1, 1, 1,
2, 2, 2, 2,
3, 3, 3, 3
)
"""
The next tile given to you is not completely random. It's drawn without
replacement from a shuffled deck with above tiles. When you run out,
another is created. I think this can be implemented in two different
ways without affect the how the game is emulated. One way is to shuffle
a copy of the list and then draw in order. Another is to pop off a
random tile.
"""
####################
# Helper Functions #
####################
def _create_bonus_deck(highest_tile=3):
"""Bonus Deck for game of Threes!
The smallest bonus tile is 6. The maximum number of tiles in a bonus
deck is 3. The values of the bonus deck tiles are 1/8, 1/16 and 1/32
of the highest tile value on the board. If any of the bonus deck
tile values would be smaller than 6, no bonus tile is created for
that tile slot in the bonus deck.
e.g. highest_tile=3 => bonus_deck = []
e.g. highest_tile=12 => bonus_deck = []
e.g. highest_tile=48 => bonus_deck = [6]
e.g. highest_tile=96 => bonus_deck = [12, 6]
e.g. highest_tile=192 => bonus_deck = [24, 12, 6]
e.g. highest_tile=384 => bonus_deck = [48, 24, 12]
"""
bonus_deck = []
while highest_tile >= 48 and len(bonus_deck) < 3:
bonus_deck.append(highest_tile//8)
highest_tile //= 2
return bonus_deck
def _create_deck(highest_tile=3):
"""Tiles to come in a game of Threes.
The standard deck are two base decks that are shuffled and then
combined
"""
deck = []
bonus_deck = _create_bonus_deck(highest_tile)
if bonus_deck:
deck.append(bonus_deck)
# Prevent too many of the same tile in a row too often
sequence1 = list(_BASE)
sequence2 = list(_BASE)
random.shuffle(sequence1)
random.shuffle(sequence2)
# original deck can be [] or contain a bonus tile
deck += sequence1 + sequence2
return deck
def _create_deck_2():
"""Tiles in a Threes! tile deck
It represent all the tiles that can be drawn from a fresh deck.
"""
return list(_BASE)
##################
# TileDeck Class #
##################
class TileDeck(object):
"""Tile deck used in a game of Threes"""
def __init__(self, existing_deck=None, highest_tile=3):
"""Generating a tile deck to be used in a game of threes
highest_tile: depending on the highest score in game
existing_deck: normally empty - starting a new game
if not empty - it means it's starting an previous
game, and need the old deck again
"""
if existing_deck:
self.deck = existing_deck
else:
self.deck = _create_deck(highest_tile)
def get_next_tile(self, highest_tile=3):
"""Get the next tile in FIFO order due to bonus tile placement
highest_tile: on the game board, the max bonus is 1/8 of it
"""
try:
return self.deck.pop(0)
except IndexError:
# Make a draw a new deck if the old one runs out
self.deck = _create_deck(highest_tile)
return self.deck.pop(0)
def __str__(self):
"""A peek at the deck"""
return '<Current Deck: ' + str(self.deck) + '>'
def __repr__(self):
"""Tile Deck as a string"""
return 'TileDeck(' + str(self.deck) + ')'
def __eq__(self, other):
"""Check equality"""
return self.deck == other.deck
# Basic Testing
if __name__ == '__main__':
# Imports
# TODO: Create basic testing facility using one of Python's builtin
# test framework
# Simplest test
deck = TileDeck()
print(len(deck.deck))
print(deck.deck)
def simpleTest():
deck0 = TileDeck()
deck1 = TileDeck([], 3) # new deck
deck2 = TileDeck([], 3) # wrong bonus
deck3 = TileDeck([1, 2, 3], 12) # right bonus, existing deck
deck4 = TileDeck([], 3)
deck5 = TileDeck([], 192) # right bonus
print(deck0)
print(deck1)
print(deck2)
print(deck3)
print(deck4)
print(deck5)
print(repr(deck1))
print(str(deck1))
deck5 = eval(repr(deck2))
assert deck5 == deck2
for i in range(len(deck2.deck)):
print(deck2.get_next_tile(), end=' ')
print(deck2.get_next_tile())
simpleTest()