rev |
line source |
paulo@0
|
1 /* Piece randomizers for LOCKJAW, an implementation of the Soviet Mind Game
|
paulo@0
|
2
|
paulo@0
|
3 Copyright (C) 2006-2007 Damian Yerrick <tepples+lj@spamcop.net>
|
paulo@0
|
4
|
paulo@0
|
5 This work is free software; you can redistribute it and/or modify
|
paulo@0
|
6 it under the terms of the GNU General Public License as published by
|
paulo@0
|
7 the Free Software Foundation; either version 2 of the License, or
|
paulo@0
|
8 (at your option) any later version.
|
paulo@0
|
9
|
paulo@0
|
10 This program is distributed in the hope that it will be useful,
|
paulo@0
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
paulo@0
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
paulo@0
|
13 GNU General Public License for more details.
|
paulo@0
|
14
|
paulo@0
|
15 You should have received a copy of the GNU General Public License
|
paulo@0
|
16 along with this program; if not, write to the Free Software
|
paulo@0
|
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
paulo@0
|
18
|
paulo@0
|
19 Original game concept and design by Alexey Pajitnov.
|
paulo@0
|
20 The Software is not sponsored or endorsed by Alexey Pajitnov, Elorg,
|
paulo@0
|
21 or The Tetris Company LLC.
|
paulo@0
|
22
|
paulo@0
|
23 */
|
paulo@0
|
24
|
paulo@0
|
25 #include "lj.h"
|
paulo@0
|
26
|
paulo@0
|
27 // Order matters. The history randomizers prefer to give out the
|
paulo@0
|
28 // front half early in the game. You don't want to give an S or Z
|
paulo@0
|
29 // first, or you force a slide. You also don't want to give an O
|
paulo@0
|
30 // first, or you force a slide with OS or OZ.
|
paulo@0
|
31
|
paulo@0
|
32 static const signed char piecesNoI[] = {
|
paulo@0
|
33 1, 2, 5, // front half: J L T
|
paulo@0
|
34 3, 4, 6 // back half: O S Z
|
paulo@0
|
35 };
|
paulo@0
|
36
|
paulo@0
|
37 static const signed char piecesTetrominoes[] = {
|
paulo@0
|
38 0, 1, 2, 5, // front half: I J L T
|
paulo@0
|
39 3, 4, 6 // back half: O S Z
|
paulo@0
|
40 };
|
paulo@0
|
41
|
paulo@0
|
42 static const signed char piecesWithSmall[] = {
|
paulo@0
|
43 0, 1, 2, 5, 9, // front half: I J L T L3
|
paulo@0
|
44 3, 7, 8, 4, 6 // back half: O I2 I3 S Z
|
paulo@0
|
45 // make sure that the S and Z are in the back half
|
paulo@0
|
46 };
|
paulo@0
|
47
|
paulo@0
|
48 static const signed char piecesSZ[] = {
|
paulo@0
|
49 4, 6
|
paulo@0
|
50 };
|
paulo@0
|
51
|
paulo@0
|
52 static const signed char piecesI[] = {
|
paulo@0
|
53 0
|
paulo@0
|
54 };
|
paulo@0
|
55
|
paulo@0
|
56 static const signed char piecesT[] = {
|
paulo@0
|
57 5
|
paulo@0
|
58 };
|
paulo@0
|
59
|
paulo@0
|
60 static const signed char *const pieceSets[] = {
|
paulo@0
|
61 piecesTetrominoes,
|
paulo@0
|
62 piecesNoI,
|
paulo@0
|
63 piecesSZ,
|
paulo@0
|
64 piecesI,
|
paulo@0
|
65 piecesWithSmall,
|
paulo@0
|
66 piecesT
|
paulo@0
|
67 };
|
paulo@0
|
68
|
paulo@0
|
69 static const unsigned char pieceSetSizes[] = {
|
paulo@0
|
70 sizeof(piecesTetrominoes),
|
paulo@0
|
71 sizeof(piecesNoI),
|
paulo@0
|
72 sizeof(piecesSZ),
|
paulo@0
|
73 sizeof(piecesI),
|
paulo@0
|
74 sizeof(piecesWithSmall),
|
paulo@0
|
75 sizeof(piecesT)
|
paulo@0
|
76 };
|
paulo@0
|
77
|
paulo@0
|
78 static void bagInitRandomize(LJField *p) {
|
paulo@0
|
79 p->permuPhase = 0;
|
paulo@0
|
80 }
|
paulo@0
|
81
|
paulo@0
|
82 static unsigned int bagRandomize(LJField *p) {
|
paulo@0
|
83 unsigned int setNo = p->pieceSet;
|
paulo@0
|
84 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
85 const signed char *set = pieceSets[setNo];
|
paulo@0
|
86
|
paulo@0
|
87 int piece;
|
paulo@0
|
88
|
paulo@0
|
89 // If we're out of permutation pieces, make new ones by copying
|
paulo@0
|
90 // from one of the rand#Bag arrays until we hit the -1 sentinel.
|
paulo@0
|
91 if (p->permuPhase == 0) {
|
paulo@0
|
92 int pos;
|
paulo@0
|
93
|
paulo@0
|
94 for (pos = 0; pos < len; ++pos) {
|
paulo@0
|
95 p->permuPiece[pos] = set[pos];
|
paulo@0
|
96 p->permuPiece[pos + len] = set[pos];
|
paulo@0
|
97 }
|
paulo@0
|
98
|
paulo@0
|
99 // Double bag: Add one randomly chosen piece to the bag
|
paulo@0
|
100 if (p->randomizer == LJRAND_2BAG) {
|
paulo@0
|
101 pos *= 2;
|
paulo@0
|
102 }
|
paulo@0
|
103 // 7+1 Bag (TOJ style): Add one randomly chosen piece to the bag
|
paulo@0
|
104 else if (p->randomizer == LJRAND_BAGPLUS1) {
|
paulo@0
|
105 p->permuPiece[pos++] = set[ljRand(p) % len];
|
paulo@0
|
106 }
|
paulo@0
|
107 p->permuPhase = pos;
|
paulo@0
|
108 }
|
paulo@0
|
109
|
paulo@0
|
110 // Choose a position in the remainder of the deck
|
paulo@0
|
111 int r = (p->permuPhase > 1) ? ljRand(p) % p->permuPhase : 0;
|
paulo@0
|
112
|
paulo@0
|
113 // Swap the top card with the card at this position
|
paulo@0
|
114 piece = p->permuPiece[r];
|
paulo@0
|
115 p->permuPiece[r] = p->permuPiece[--p->permuPhase];
|
paulo@0
|
116 return piece;
|
paulo@0
|
117 }
|
paulo@0
|
118
|
paulo@0
|
119 static void mtbInitRandomize(LJField *p) {
|
paulo@0
|
120 unsigned int setNo = p->pieceSet;
|
paulo@0
|
121 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
122 const signed char *set = pieceSets[setNo];
|
paulo@0
|
123
|
paulo@0
|
124 // At game start, front three are I, J, L
|
paulo@0
|
125 // Back four (never dealt as first tetromino) are O, S, T, Z
|
paulo@0
|
126 for (int i = 0; i < len; ++i) {
|
paulo@0
|
127 p->permuPiece[i] = set[i];
|
paulo@0
|
128 }
|
paulo@0
|
129 }
|
paulo@0
|
130
|
paulo@0
|
131 static unsigned int mtbRandomize(LJField *p) {
|
paulo@0
|
132 unsigned int setNo = p->pieceSet;
|
paulo@0
|
133 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
134
|
paulo@0
|
135 // Choose a piece from the three in front
|
paulo@0
|
136 int r = ljRand(p) % (len / 2);
|
paulo@0
|
137 int piece = p->permuPiece[r];
|
paulo@0
|
138
|
paulo@0
|
139 // Move it to the back
|
paulo@0
|
140 for (; r < len - 1; ++r) {
|
paulo@0
|
141 p->permuPiece[r] = p->permuPiece[r + 1];
|
paulo@0
|
142 }
|
paulo@0
|
143 p->permuPiece[len - 1] = piece;
|
paulo@0
|
144 return piece;
|
paulo@0
|
145 }
|
paulo@0
|
146
|
paulo@0
|
147 static void tgmInitRandomize(LJField *p) {
|
paulo@0
|
148 unsigned int setNo = p->pieceSet;
|
paulo@0
|
149 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
150
|
paulo@0
|
151 p->permuPiece[0] = 0;
|
paulo@0
|
152 for (int i = len / 2; i < len; ++i) {
|
paulo@0
|
153 p->permuPiece[i] = i & 1 ? LJP_Z : LJP_S;
|
paulo@0
|
154 }
|
paulo@0
|
155 }
|
paulo@0
|
156
|
paulo@0
|
157 /* State of TGM randomizer:
|
paulo@0
|
158 * permuPiece[3..6]: history, [3] most recent
|
paulo@0
|
159 * permuPiece[2]: 0 for first piece (use equal probability I, J, L, T)
|
paulo@0
|
160 * or 1 for subsequent pieces
|
paulo@0
|
161 */
|
paulo@0
|
162 static unsigned int tgmRandomize(LJField *p) {
|
paulo@0
|
163 unsigned int setNo = p->pieceSet;
|
paulo@0
|
164 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
165 const signed char *set = pieceSets[setNo];
|
paulo@0
|
166
|
paulo@0
|
167 int r = ljRand(p) ^ (ljRand(p) << 15);
|
paulo@0
|
168 int piece;
|
paulo@0
|
169
|
paulo@0
|
170 if (p->permuPiece[0]) {
|
paulo@0
|
171
|
paulo@0
|
172 // Roll up to 6 times for pieces
|
paulo@0
|
173 for (int rolls = 6;
|
paulo@0
|
174 rolls > 0;
|
paulo@0
|
175 --rolls, r /= len) {
|
paulo@0
|
176 piece = set[r % len];
|
paulo@0
|
177 int found = 0;
|
paulo@0
|
178
|
paulo@0
|
179 // If the piece is not in the history, use it now
|
paulo@0
|
180 for (int histoPos = len / 2;
|
paulo@0
|
181 !found && histoPos < len;
|
paulo@0
|
182 ++histoPos) {
|
paulo@0
|
183 if (piece == p->permuPiece[histoPos]) {
|
paulo@0
|
184 found = 1;
|
paulo@0
|
185 }
|
paulo@0
|
186 }
|
paulo@0
|
187 if (!found) {
|
paulo@0
|
188 break;
|
paulo@0
|
189 }
|
paulo@0
|
190 }
|
paulo@0
|
191 } else {
|
paulo@0
|
192 p->permuPiece[0] = 1;
|
paulo@0
|
193 // Generate only pieces in the first half of the list
|
paulo@0
|
194 piece = set[r % ((len + 1) / 2)];
|
paulo@0
|
195 if (piece == LJP_O) {
|
paulo@0
|
196 piece = LJP_T;
|
paulo@0
|
197 }
|
paulo@0
|
198 }
|
paulo@0
|
199
|
paulo@0
|
200 // Move it to the back
|
paulo@0
|
201 for (r = len / 2; r < len - 1; ++r) {
|
paulo@0
|
202 p->permuPiece[r] = p->permuPiece[r + 1];
|
paulo@0
|
203 }
|
paulo@0
|
204 p->permuPiece[len - 1] = piece;
|
paulo@0
|
205 return piece;
|
paulo@0
|
206 }
|
paulo@0
|
207
|
paulo@0
|
208 void initRandomize(LJField *p) {
|
paulo@0
|
209 unsigned int setNo = p->pieceSet;
|
paulo@0
|
210 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
211
|
paulo@0
|
212 if (len < 2) {
|
paulo@0
|
213 p->randomizer = LJRAND_PURE;
|
paulo@0
|
214 }
|
paulo@0
|
215
|
paulo@0
|
216 switch (p->randomizer) {
|
paulo@0
|
217 case LJRAND_PURE:
|
paulo@0
|
218 break;
|
paulo@0
|
219 case LJRAND_BAG:
|
paulo@0
|
220 case LJRAND_BAGPLUS1:
|
paulo@0
|
221 case LJRAND_2BAG:
|
paulo@0
|
222 bagInitRandomize(p);
|
paulo@0
|
223 break;
|
paulo@0
|
224 case LJRAND_HIST_INF:
|
paulo@0
|
225 mtbInitRandomize(p);
|
paulo@0
|
226 break;
|
paulo@0
|
227 case LJRAND_HIST_6:
|
paulo@0
|
228 tgmInitRandomize(p);
|
paulo@0
|
229 break;
|
paulo@0
|
230 }
|
paulo@0
|
231 }
|
paulo@0
|
232
|
paulo@0
|
233 unsigned int randomize(LJField *p) {
|
paulo@0
|
234 switch (p->randomizer) {
|
paulo@0
|
235
|
paulo@0
|
236 case LJRAND_BAG:
|
paulo@0
|
237 case LJRAND_BAGPLUS1:
|
paulo@0
|
238 case LJRAND_2BAG:
|
paulo@0
|
239 return bagRandomize(p);
|
paulo@0
|
240 case LJRAND_HIST_INF:
|
paulo@0
|
241 return mtbRandomize(p);
|
paulo@0
|
242 case LJRAND_HIST_6:
|
paulo@0
|
243 return tgmRandomize(p);
|
paulo@0
|
244 case LJRAND_PURE:
|
paulo@0
|
245 default:
|
paulo@0
|
246 {
|
paulo@0
|
247 unsigned int setNo = p->pieceSet;
|
paulo@0
|
248 unsigned int len = pieceSetSizes[setNo];
|
paulo@0
|
249 const signed char *set = pieceSets[setNo];
|
paulo@0
|
250 return set[ljRand(p) % len];
|
paulo@0
|
251 }
|
paulo@0
|
252 }
|
paulo@0
|
253 }
|