summaryrefslogtreecommitdiff
path: root/apps/plugins
diff options
context:
space:
mode:
authorJohannes Schwarz <ubuntuxer@rockbox.org>2009-10-24 18:54:48 +0000
committerJohannes Schwarz <ubuntuxer@rockbox.org>2009-10-24 18:54:48 +0000
commitc12e5fc86afc6595e8fcd2685de643b46c999837 (patch)
tree4b14f6e8f47263b5c0e78bc9fbbcfacc7c0ac229 /apps/plugins
parent5897249c1e4fff7e4fa096fd83d89782c3b064ac (diff)
downloadrockbox-c12e5fc86afc6595e8fcd2685de643b46c999837.tar.gz
rockbox-c12e5fc86afc6595e8fcd2685de643b46c999837.zip
FS#10497 - New game codebuster, which is a clone of the classic game mastermind. It just runs on color LCD. Thank you to the author Clément Pit--Claudel (CFP)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@23332 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps/plugins')
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES1
-rw-r--r--apps/plugins/codebuster.c514
3 files changed, 516 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 6809844efb..505f83bff1 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -14,6 +14,7 @@ chip8,viewers
14chopper,games 14chopper,games
15clix,games 15clix,games
16clock,apps 16clock,apps
17codebuster,games
17credits,viewers 18credits,viewers
18cube,demos 19cube,demos
19demystify,demos 20demystify,demos
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index d89b0b6e5f..82dd4b78e4 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -67,6 +67,7 @@ robotfindskitten.c
67#ifdef HAVE_LCD_COLOR 67#ifdef HAVE_LCD_COLOR
68clix.c 68clix.c
69ppmviewer.c 69ppmviewer.c
70codebuster.c
70#endif 71#endif
71 72
72/* Plugins needing the grayscale lib on low-depth LCDs */ 73/* Plugins needing the grayscale lib on low-depth LCDs */
diff --git a/apps/plugins/codebuster.c b/apps/plugins/codebuster.c
new file mode 100644
index 0000000000..edcb7e7904
--- /dev/null
+++ b/apps/plugins/codebuster.c
@@ -0,0 +1,514 @@
1/***************************************************************************
2* __________ __ ___.
3* Open \______ \ ____ ____ | | _\_ |__ _______ ___
4* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7* \/ \/ \/ \/ \/
8* $Id:
9*
10* Copyright (C) 2009 Clément Pit--Claudel
11*
12* This program is free software; you can redistribute it and/or
13* modify it under the terms of the GNU General Public License
14* as published by the Free Software Foundation; either version 2
15* of the License, or (at your option) any later version.
16*
17* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18* KIND, either express or implied.
19*
20****************************************************************************/
21
22#include "plugin.h"
23#include "lib/configfile.h"
24#include "lib/playback_control.h"
25#include "lib/pluginlib_actions.h"
26
27PLUGIN_HEADER
28
29/* Limits */
30#define MAX_PIECES_COUNT 5
31#define MAX_COLORS_COUNT 8
32#define MAX_GUESSES_COUNT 10
33
34const struct button_mapping *plugin_contexts[] =
35 {generic_directions, generic_actions};
36
37/* Mapping */
38#define EXIT PLA_QUIT
39#define VALIDATE PLA_FIRE
40#define PREV_PIECE PLA_LEFT
41#define PREV_PIECE_REPEAT PLA_LEFT_REPEAT
42#define NEXT_PIECE PLA_RIGHT
43#define NEXT_PIECE_REPEAT PLA_RIGHT_REPEAT
44#define PREV_COLOR PLA_UP
45#define PREV_COLOR_REPEAT PLA_UP_REPEAT
46#define NEXT_COLOR PLA_DOWN
47#define NEXT_COLOR_REPEAT PLA_DOWN_REPEAT
48
49/*
50 * Screen structure:
51 * * (guesses_count) lines of guesses,
52 * * 1 center line of solution (hidden),
53 * * 1 line showing available colors.
54 *
55 * Status vars:
56 * * quit: exit the plugin
57 * * leave: restart the plugin (leave the current game)
58 * * game_ended: the game has ended
59 * * found: the combination has been found
60 *
61 * Colors used are taken from the Tango project.
62 *
63 * Due to integer truncations, 2 vars are used for some objects' dimensions
64 * (eg. true_guess_w, true_score_w). The actual dimension of these objects is
65 * stored in the corresponding var. without the "true" prefix.
66 */
67
68struct mm_score {
69 int correct;
70 int misplaced;
71};
72
73struct mm_line {
74 struct mm_score score;
75 int pieces[MAX_PIECES_COUNT];
76};
77
78const int colors[MAX_COLORS_COUNT] = {
79 LCD_RGBPACK(252, 233, 79),
80 LCD_RGBPACK(206, 92, 0),
81 LCD_RGBPACK(143, 89, 2),
82 LCD_RGBPACK( 78, 154, 6),
83 /* LCD_RGBPACK( 32, 74, 135), */
84 LCD_RGBPACK( 52, 101, 164),
85 /* LCD_RGBPACK(114, 159, 207), */
86 LCD_RGBPACK(117, 80, 123),
87 /* LCD_RGBPACK(173, 127, 168), */
88 LCD_RGBPACK(164, 0, 0),
89 LCD_RGBPACK(238, 238, 236),
90 };
91
92/* Flags */
93static bool quit, leave, usb;
94static bool found, game_ended;
95
96/* Settings */
97static int pieces_count;
98static int colors_count;
99static int guesses_count;
100static int pieces_tmp = 5;
101static int colors_tmp = 7;
102static int guesses_tmp = 10;
103static bool labeling = false, framing = false;
104
105/* Display */
106#define ALUMINIUM LCD_RGBPACK(136, 138, 133)
107
108#define MARGIN 5
109#define X_MARGIN (LCD_WIDTH / 20)
110#define Y_MARGIN (LCD_HEIGHT / 20)
111#define GAME_H (LCD_HEIGHT - (2 * Y_MARGIN))
112#define LINE_W (LCD_WIDTH - (2 * X_MARGIN))
113
114#define CONFIG_FILE_NAME "codebuster.cfg"
115
116static struct configdata config[] = {
117 {TYPE_INT, 0, MAX_PIECES_COUNT, { .int_p = &pieces_tmp }, "pieces", NULL},
118 {TYPE_INT, 0, MAX_COLORS_COUNT, { .int_p = &colors_tmp }, "colors", NULL},
119 {TYPE_INT, 0, MAX_GUESSES_COUNT, { .int_p = &guesses_tmp }, "guesses", NULL},
120 {TYPE_BOOL, 0, 1, { .bool_p = &labeling }, "labeling", NULL},
121 {TYPE_BOOL, 0, 1, { .bool_p = &framing }, "framing", NULL},
122};
123static bool settings_changed = false;
124
125static int line_h;
126static int piece_w, tick_w;
127static int true_guess_w, true_score_w, guess_w, score_w;
128
129/* Guesses and solution */
130struct mm_line solution, hidden;
131struct mm_line guesses[MAX_GUESSES_COUNT];
132
133/* Alias for pluginlib_getaction */
134static inline int get_button(void) {
135 return pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 2);
136}
137
138/* Computes the margin to center an element */
139static inline int get_margin(int width, int full_w) {
140 return ((full_w - width) / 2);
141}
142
143static inline bool stop_game(void) {
144 return (quit || leave || found);
145}
146
147static void fill_color_rect(int x, int y, int w, int h, int color) {
148 rb->lcd_set_foreground(color);
149 rb->lcd_fillrect(x, y, w, h);
150 rb->lcd_set_foreground(LCD_WHITE);
151}
152
153static void overfill_rect(int x, int y, int w, int h) {
154 rb->lcd_fillrect(x - 2, y - 2, w + 4, h + 4);
155}
156
157static void draw_piece(int x, int y, int w, int h, int color_id, bool emph) {
158 int color = LCD_BLACK;
159
160 if (color_id >= 0)
161 color = colors[color_id];
162 else if (color_id == -2) /* Hidden piece */
163 color = ALUMINIUM;
164
165 if (emph)
166 overfill_rect(x, y, w, h);
167
168 if (color_id == -1) /* Uninitialised color */
169 rb->lcd_drawrect(x, y, w, h);
170 else
171 fill_color_rect(x, y, w, h, color);
172
173 if (!emph && framing)
174 rb->lcd_drawrect(x, y, w, h);
175
176 if (labeling && color_id >= 0) {
177 char text[2];
178 rb->snprintf(text, 2, "%d", color_id);
179
180 int fw, fh; rb->font_getstringsize(text, &fw, &fh, FONT_SYSFIXED);
181 rb->lcd_putsxy(x + get_margin(fw, w), y + get_margin(fh, h), text);
182 }
183}
184
185/* Compute the score for a given guess (expressed in ticks) */
186static void validate_guess(struct mm_line* guess) {
187 bool solution_match[pieces_count];
188 bool guess_match[pieces_count];
189
190 guess->score.misplaced = 0;
191 guess->score.correct = 0;
192
193 int guess_pos;
194
195 /* Initialisation with 0s */
196 for (guess_pos = 0; guess_pos < pieces_count; guess_pos++)
197 solution_match[guess_pos] = guess_match[guess_pos] = false;
198
199 /* 1st step : detect correctly positioned pieces */
200 for (guess_pos = 0; guess_pos < pieces_count; guess_pos++) {
201 if (solution.pieces[guess_pos] == guess->pieces[guess_pos]) {
202 guess->score.correct += 1;
203
204 guess_match[guess_pos] = solution_match[guess_pos]
205 = true;
206 }
207 }
208
209 /* Second step : detect mispositioned pieces */
210 for (guess_pos = 0; guess_pos < pieces_count; guess_pos++) {
211 if (guess_match[guess_pos]) continue;
212
213 int sol_pos;
214 for (sol_pos = 0; sol_pos < pieces_count; sol_pos++) {
215 if (guess_match[guess_pos]) break;
216 if (solution_match[sol_pos]) continue;
217
218 if (guess->pieces[guess_pos] == solution.pieces[sol_pos]) {
219 guess->score.misplaced += 1;
220
221 solution_match[sol_pos] = true;
222 break;
223 }
224 }
225 }
226}
227
228static void draw_guess(int line, struct mm_line* guess, int cur_guess,
229 int cur_piece, bool show_score) {
230 int cur_y = (Y_MARGIN + MARGIN) + 2 * line_h * line;
231 int l_margin = X_MARGIN + (show_score ? 0 : get_margin(guess_w, LINE_W));
232
233 int piece;
234 for (piece = 0; piece < pieces_count; piece++) {
235 int cur_x = l_margin + 2 * piece_w * piece;
236 draw_piece(cur_x, cur_y, piece_w, line_h, guess->pieces[piece],
237 line == cur_guess && piece == cur_piece);
238 }
239}
240
241static void draw_score(int line, struct mm_line* guess) {
242 int cur_y = Y_MARGIN + 2 * line_h * line;
243 int l_margin = X_MARGIN + true_guess_w + MARGIN;
244
245 int tick = 0;
246 for (; tick < guess->score.correct; tick++) {
247 int cur_x = l_margin + 2 * tick_w * tick;
248
249 fill_color_rect(cur_x, cur_y, tick_w, line_h, LCD_RGBPACK(239, 41, 41));
250 }
251
252 for (; tick < guess->score.correct + guess->score.misplaced; tick++) {
253 int cur_x = l_margin + 2 * tick_w * tick;
254
255 fill_color_rect(cur_x, cur_y, tick_w, line_h, LCD_RGBPACK(211, 215, 207));
256 }
257}
258
259static void draw_board(int cur_guess, int cur_piece) {
260 rb->lcd_clear_display();
261
262 int line = 0;
263 for (; line < guesses_count; line++) {
264 draw_guess(line, &guesses[line], cur_guess, cur_piece, true);
265 if (line < cur_guess) draw_score(line, &guesses[line]);
266 }
267
268 int color;
269 int colors_margin = 2;
270 int cur_y = (Y_MARGIN + MARGIN) + 2 * line_h * line;
271 int color_w = (LINE_W - colors_margin * (colors_count - 1)) / colors_count;
272
273 for (color = 0; color < colors_count; color++) {
274 int cur_x = X_MARGIN + color * (color_w + colors_margin);
275 draw_piece(cur_x, cur_y, color_w, line_h, color,
276 color == guesses[cur_guess].pieces[cur_piece]);
277 }
278
279 line++;
280
281 if(game_ended)
282 draw_guess(line, &solution, cur_guess, cur_piece, false);
283 else
284 draw_guess(line, &hidden, cur_guess, cur_piece, false);
285
286 rb->lcd_update();
287}
288
289static void init_vars(void) {
290 quit = leave = usb = found = game_ended = false;
291
292 int guess, piece;
293 for (guess = 0; guess < guesses_count; guess++) {
294 for (piece = 0; piece < pieces_count; piece++)
295 guesses[guess].pieces[piece] = -1;
296 }
297 for (piece = 0; piece < pieces_count; piece++) {
298 guesses[0].pieces[piece] = 0;
299 hidden.pieces[piece] = -2;
300 }
301}
302
303static void init_board(void) {
304
305 pieces_count = pieces_tmp;
306 colors_count = colors_tmp;
307 guesses_count = guesses_tmp;
308
309 line_h = GAME_H / (2 * (guesses_count + 2) - 1);
310
311 true_score_w = LINE_W * 0.25;
312 true_guess_w = LINE_W - (true_score_w + MARGIN);
313
314 tick_w = true_score_w / (2 * pieces_count - 1);
315 piece_w = true_guess_w / (2 * pieces_count - 1);
316
317 /* Readjust (due to integer divisions) */
318 score_w = tick_w * (2 * pieces_count - 1);
319 guess_w = piece_w * (2 * pieces_count - 1);
320}
321
322static void randomize_solution(void) {
323 int piece_id;
324 for (piece_id = 0; piece_id < pieces_count; piece_id++)
325 solution.pieces[piece_id] = rb->rand() % colors_count;
326}
327
328static void settings_menu(void) {
329 MENUITEM_STRINGLIST(settings_menu, "Settings", NULL,
330 "Number of colors", "Number of pieces",
331 "Number of guesses", "Labels ?", "Frames ?");
332
333 int cur_item =0;
334
335 bool menu_quit = false;
336 while(!menu_quit) {
337
338 switch(rb->do_menu(&settings_menu, &cur_item, NULL, false)) {
339 case 0:
340 rb->set_int("Number of colors", "", UNIT_INT, &colors_tmp,
341 NULL, -1, MAX_COLORS_COUNT, 1, NULL);
342 break;
343 case 1:
344 rb->set_int("Number of pieces", "", UNIT_INT, &pieces_tmp,
345 NULL, -1, MAX_PIECES_COUNT, 1, NULL);
346 break;
347 case 2:
348 rb->set_int("Number of guesses", "", UNIT_INT, &guesses_tmp,
349 NULL, -1, MAX_GUESSES_COUNT, 1, NULL);
350 break;
351 case 3:
352 rb->set_bool("Display labels ?", &labeling);
353 break;
354 case 4:
355 rb->set_bool("Display frames ?", &framing);
356 break;
357 case GO_TO_PREVIOUS:
358 menu_quit = true;
359 break;
360 default:
361 break;
362 }
363 }
364}
365
366static bool resume;
367static int menu_cb(int action, const struct menu_item_ex *this_item)
368{
369 int i = ((intptr_t)this_item);
370 if ((action == ACTION_REQUEST_MENUITEM) && (!resume && (i==0)))
371 return ACTION_EXIT_MENUITEM;
372 return action;
373}
374
375static void main_menu(void) {
376 MENUITEM_STRINGLIST(main_menu, "Codebuster Menu", menu_cb,
377 "Resume Game", "Start New Game", "Settings",
378 "Playback Control", "Quit");
379
380 int cur_item =0;
381
382 bool menu_quit = false;
383 while(!menu_quit) {
384
385 switch(rb->do_menu(&main_menu, &cur_item, NULL, false)) {
386 case 0:
387 resume = true;
388 menu_quit = true;
389 break;
390 case 1:
391 leave = true;
392 menu_quit = true;
393 break;
394 case 2:
395 settings_menu();
396 settings_changed = true;
397 break;
398 case 3:
399 playback_control(NULL);
400 break;
401 case 4:
402 quit = menu_quit = true;
403 break;
404 case MENU_ATTACHED_USB:
405 usb = menu_quit = true;
406 break;
407 default:
408 break;
409 }
410 }
411}
412
413enum plugin_status plugin_start(const void* parameter) {
414 (void)parameter;
415
416 rb->srand(*rb->current_tick);
417 rb->lcd_setfont(FONT_SYSFIXED);
418 rb->lcd_set_backdrop(NULL);
419 rb->lcd_set_foreground(LCD_WHITE);
420 rb->lcd_set_background(LCD_BLACK);
421
422 configfile_load(CONFIG_FILE_NAME,config,5,0);
423
424 main_menu();
425 while (!quit) {
426 init_board();
427 randomize_solution();
428 init_vars();
429
430 draw_board(0, 0);
431 int button = 0, guess = 0, piece = 0;
432 for (guess = 0; guess < guesses_count && !stop_game(); guess++) {
433 while(!stop_game()) {
434 draw_board(guess, piece);
435
436 if ((button = get_button()) == VALIDATE) break;
437
438 switch (button) {
439
440 case EXIT:
441 resume = true;
442 main_menu();
443 break;
444
445 case NEXT_PIECE:
446 case NEXT_PIECE_REPEAT:
447 piece = (piece + 1) % pieces_count;
448 break;
449
450 case PREV_PIECE:
451 case PREV_PIECE_REPEAT:
452 piece = (piece + pieces_count - 1) % pieces_count;
453 break;
454
455
456 case NEXT_COLOR:
457 case NEXT_COLOR_REPEAT:
458 guesses[guess].pieces[piece] =
459 (guesses[guess].pieces[piece] + 1)
460 % colors_count;
461 break;
462
463 case PREV_COLOR:
464 case PREV_COLOR_REPEAT:
465 guesses[guess].pieces[piece] =
466 (guesses[guess].pieces[piece] + colors_count - 1)
467 % colors_count;
468 break;
469
470 default:
471 if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
472 quit = usb = true;
473 }
474
475 if (guesses[guess].pieces[piece] == -1)
476 guesses[guess].pieces[piece] = 0;
477 }
478
479 if (!quit) {
480 validate_guess(&guesses[guess]);
481
482 if (guesses[guess].score.correct == pieces_count)
483 found = true;
484
485 if (guess + 1 < guesses_count && !found)
486 guesses[guess + 1] = guesses[guess];
487 }
488 }
489
490 game_ended = true;
491 resume = false;
492 if (!quit && !leave) {
493 draw_board(guess, piece);
494
495 if (found)
496 rb->splash(HZ, "Well done :)");
497 else
498 rb->splash(HZ, "Wooops :(");
499 do {
500 button = rb->button_get(true);
501 if (rb->default_event_handler(button) == SYS_USB_CONNECTED) {
502 quit = usb = true;
503 }
504 } while( ( button == BUTTON_NONE )
505 || ( button & (BUTTON_REL|BUTTON_REPEAT) ) );
506 main_menu();
507 }
508 }
509 if (settings_changed)
510 configfile_save(CONFIG_FILE_NAME,config,5,0);
511
512 rb->lcd_setfont(FONT_UI);
513 return (usb) ? PLUGIN_USB_CONNECTED : PLUGIN_OK;
514}