1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2009 Clément Pit--Claudel
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
23 #include "lib/configfile.h"
24 #include "lib/playback_control.h"
25 #include "lib/pluginlib_actions.h"
30 #define MAX_PIECES_COUNT 5
31 #define MAX_COLORS_COUNT 8
32 #define MAX_GUESSES_COUNT 10
34 const struct button_mapping
*plugin_contexts
[] = {
36 #ifdef HAVE_REMOTE_LCD
43 * * (guesses_count) lines of guesses,
44 * * 1 center line of solution (hidden),
45 * * 1 line showing available colors.
48 * * quit: exit the plugin
49 * * leave: restart the plugin (leave the current game)
50 * * game_ended: the game has ended
51 * * found: the combination has been found
53 * Colors used are taken from the Tango project.
55 * Due to integer truncations, 2 vars are used for some objects' dimensions
56 * (eg. true_guess_w, true_score_w). The actual dimension of these objects is
57 * stored in the corresponding var. without the "true" prefix.
66 struct mm_score score
;
67 int pieces
[MAX_PIECES_COUNT
];
70 const int colors
[MAX_COLORS_COUNT
] = {
71 LCD_RGBPACK(252, 233, 79),
72 LCD_RGBPACK(206, 92, 0),
73 LCD_RGBPACK(143, 89, 2),
74 LCD_RGBPACK( 78, 154, 6),
75 /* LCD_RGBPACK( 32, 74, 135), */
76 LCD_RGBPACK( 52, 101, 164),
77 /* LCD_RGBPACK(114, 159, 207), */
78 LCD_RGBPACK(117, 80, 123),
79 /* LCD_RGBPACK(173, 127, 168), */
80 LCD_RGBPACK(164, 0, 0),
81 LCD_RGBPACK(238, 238, 236),
85 static bool quit
, leave
, usb
;
86 static bool found
, game_ended
;
96 static struct settings settings
= {
97 5, 7, 10, false, false,
99 static struct settings old_settings
;
100 static int pieces_count
;
101 static int colors_count
;
102 static int guesses_count
;
105 #define ALUMINIUM LCD_RGBPACK(136, 138, 133)
108 #define X_MARGIN (LCD_WIDTH / 20)
109 #define Y_MARGIN (LCD_HEIGHT / 20)
110 #define GAME_H (LCD_HEIGHT - (2 * Y_MARGIN))
111 #define LINE_W (LCD_WIDTH - (2 * X_MARGIN))
113 #define CONFIG_FILE_NAME "codebuster.cfg"
115 static struct configdata config
[] = {
116 {TYPE_INT
, 0, MAX_PIECES_COUNT
, { .int_p
= &settings
.pieces
}, "pieces", NULL
},
117 {TYPE_INT
, 0, MAX_COLORS_COUNT
, { .int_p
= &settings
.colors
}, "colors", NULL
},
118 {TYPE_INT
, 0, MAX_GUESSES_COUNT
, { .int_p
= &settings
.guesses
}, "guesses", NULL
},
119 {TYPE_BOOL
, 0, 1, { .bool_p
= &settings
.labeling
}, "labeling", NULL
},
120 {TYPE_BOOL
, 0, 1, { .bool_p
= &settings
.framing
}, "framing", NULL
},
124 static int piece_w
, tick_w
;
125 static int true_guess_w
, true_score_w
, guess_w
, score_w
;
127 /* Guesses and solution */
128 struct mm_line solution
, hidden
;
129 struct mm_line guesses
[MAX_GUESSES_COUNT
];
131 /* Alias for pluginlib_getaction */
132 static inline int get_button(void) {
133 return pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
,
134 ARRAYLEN(plugin_contexts
));
137 /* Computes the margin to center an element */
138 static inline int get_margin(int width
, int full_w
) {
139 return ((full_w
- width
) / 2);
142 static inline bool stop_game(void) {
143 return (quit
|| leave
|| found
);
146 static void fill_color_rect(int x
, int y
, int w
, int h
, int color
) {
147 rb
->lcd_set_foreground(color
);
148 rb
->lcd_fillrect(x
, y
, w
, h
);
149 rb
->lcd_set_foreground(LCD_WHITE
);
152 static void overfill_rect(int x
, int y
, int w
, int h
) {
153 rb
->lcd_fillrect(x
- 2, y
- 2, w
+ 4, h
+ 4);
156 static void draw_piece(int x
, int y
, int w
, int h
, int color_id
, bool emph
) {
157 int color
= LCD_BLACK
;
160 color
= colors
[color_id
];
161 else if (color_id
== -2) /* Hidden piece */
165 overfill_rect(x
, y
, w
, h
);
167 if (color_id
== -1) /* Uninitialised color */
168 rb
->lcd_drawrect(x
, y
, w
, h
);
170 fill_color_rect(x
, y
, w
, h
, color
);
172 if (!emph
&& settings
.framing
)
173 rb
->lcd_drawrect(x
, y
, w
, h
);
175 if (settings
.labeling
&& color_id
>= 0) {
177 rb
->snprintf(text
, sizeof(text
), "%d", color_id
);
179 int fw
, fh
; rb
->font_getstringsize(text
, &fw
, &fh
, FONT_SYSFIXED
);
180 rb
->lcd_putsxy(x
+ get_margin(fw
, w
), y
+ get_margin(fh
, h
), text
);
184 /* Compute the score for a given guess (expressed in ticks) */
185 static void validate_guess(struct mm_line
* guess
) {
186 bool solution_match
[MAX_PIECES_COUNT
];
187 bool guess_match
[MAX_PIECES_COUNT
];
189 guess
->score
.misplaced
= 0;
190 guess
->score
.correct
= 0;
194 /* Initialisation with 0s */
195 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++)
196 solution_match
[guess_pos
] = guess_match
[guess_pos
] = false;
198 /* 1st step : detect correctly positioned pieces */
199 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
200 if (solution
.pieces
[guess_pos
] == guess
->pieces
[guess_pos
]) {
201 guess
->score
.correct
+= 1;
203 guess_match
[guess_pos
] = solution_match
[guess_pos
]
208 /* Second step : detect mispositioned pieces */
209 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
210 if (guess_match
[guess_pos
]) continue;
213 for (sol_pos
= 0; sol_pos
< pieces_count
; sol_pos
++) {
214 if (guess_match
[guess_pos
]) break;
215 if (solution_match
[sol_pos
]) continue;
217 if (guess
->pieces
[guess_pos
] == solution
.pieces
[sol_pos
]) {
218 guess
->score
.misplaced
+= 1;
220 solution_match
[sol_pos
] = true;
227 static void draw_guess(int line
, struct mm_line
* guess
, int cur_guess
,
228 int cur_piece
, bool show_score
) {
229 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
230 int l_margin
= X_MARGIN
+ (show_score
? 0 : get_margin(guess_w
, LINE_W
));
233 for (piece
= 0; piece
< pieces_count
; piece
++) {
234 int cur_x
= l_margin
+ 2 * piece_w
* piece
;
235 draw_piece(cur_x
, cur_y
, piece_w
, line_h
, guess
->pieces
[piece
],
236 line
== cur_guess
&& piece
== cur_piece
);
240 static void draw_score(int line
, struct mm_line
* guess
) {
241 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
242 int l_margin
= X_MARGIN
+ true_guess_w
+ MARGIN
;
245 for (; tick
< guess
->score
.correct
; tick
++) {
246 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
248 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(239, 41, 41));
251 for (; tick
< guess
->score
.correct
+ guess
->score
.misplaced
; tick
++) {
252 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
254 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(211, 215, 207));
258 static void draw_board(int cur_guess
, int cur_piece
) {
259 rb
->lcd_clear_display();
262 for (; line
< guesses_count
; line
++) {
263 draw_guess(line
, &guesses
[line
], cur_guess
, cur_piece
, true);
264 if (line
< cur_guess
) draw_score(line
, &guesses
[line
]);
268 int colors_margin
= 2;
269 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
270 int color_w
= (LINE_W
- colors_margin
* (colors_count
- 1)) / colors_count
;
272 for (color
= 0; color
< colors_count
; color
++) {
273 int cur_x
= X_MARGIN
+ color
* (color_w
+ colors_margin
);
274 draw_piece(cur_x
, cur_y
, color_w
, line_h
, color
,
275 color
== guesses
[cur_guess
].pieces
[cur_piece
]);
281 draw_guess(line
, &solution
, cur_guess
, cur_piece
, false);
283 draw_guess(line
, &hidden
, cur_guess
, cur_piece
, false);
288 static void init_vars(void) {
289 quit
= leave
= usb
= found
= game_ended
= false;
292 for (guess
= 0; guess
< guesses_count
; guess
++) {
293 for (piece
= 0; piece
< pieces_count
; piece
++)
294 guesses
[guess
].pieces
[piece
] = -1;
296 for (piece
= 0; piece
< pieces_count
; piece
++) {
297 guesses
[0].pieces
[piece
] = 0;
298 hidden
.pieces
[piece
] = -2;
302 static void init_board(void) {
304 pieces_count
= settings
.pieces
;
305 colors_count
= settings
.colors
;
306 guesses_count
= settings
.guesses
;
308 line_h
= GAME_H
/ (2 * (guesses_count
+ 2) - 1);
310 true_score_w
= LINE_W
* 0.25;
311 true_guess_w
= LINE_W
- (true_score_w
+ MARGIN
);
313 tick_w
= true_score_w
/ (2 * pieces_count
- 1);
314 piece_w
= true_guess_w
/ (2 * pieces_count
- 1);
316 /* Readjust (due to integer divisions) */
317 score_w
= tick_w
* (2 * pieces_count
- 1);
318 guess_w
= piece_w
* (2 * pieces_count
- 1);
321 static void randomize_solution(void) {
323 for (piece_id
= 0; piece_id
< pieces_count
; piece_id
++)
324 solution
.pieces
[piece_id
] = rb
->rand() % colors_count
;
327 static void settings_menu(void) {
328 MENUITEM_STRINGLIST(settings_menu
, "Settings", NULL
,
329 "Number of colours", "Number of pegs",
331 "Display labels", "Display frames");
333 bool menu_quit
= false;
337 switch(rb
->do_menu(&settings_menu
, &cur_item
, NULL
, false)) {
339 rb
->set_int("Number of colours", "", UNIT_INT
, &settings
.colors
,
340 NULL
, -1, MAX_COLORS_COUNT
, 1, NULL
);
343 rb
->set_int("Number of pegs", "", UNIT_INT
, &settings
.pieces
,
344 NULL
, -1, MAX_PIECES_COUNT
, 1, NULL
);
347 rb
->set_int("Number of guesses", "", UNIT_INT
, &settings
.guesses
,
348 NULL
, -1, MAX_GUESSES_COUNT
, 1, NULL
);
351 rb
->set_bool("Display labels", &settings
.labeling
);
354 rb
->set_bool("Display frames", &settings
.framing
);
366 static int menu_cb(int action
, const struct menu_item_ex
*this_item
)
368 int i
= ((intptr_t)this_item
);
369 if ((action
== ACTION_REQUEST_MENUITEM
) && (!resume
&& (i
==0)))
370 return ACTION_EXIT_MENUITEM
;
374 static void main_menu(void) {
375 MENUITEM_STRINGLIST(main_menu
, "Codebuster Menu", menu_cb
,
376 "Resume Game", "Start New Game", "Settings",
377 "Playback Control", "Quit");
379 bool menu_quit
= false;
383 switch(rb
->do_menu(&main_menu
, &cur_item
, NULL
, false)) {
396 playback_control(NULL
);
399 quit
= menu_quit
= true;
401 case MENU_ATTACHED_USB
:
402 usb
= menu_quit
= true;
410 enum plugin_status
plugin_start(const void* parameter
) {
413 rb
->srand(*rb
->current_tick
);
414 rb
->lcd_setfont(FONT_SYSFIXED
);
415 rb
->lcd_set_backdrop(NULL
);
416 rb
->lcd_set_foreground(LCD_WHITE
);
417 rb
->lcd_set_background(LCD_BLACK
);
419 configfile_load(CONFIG_FILE_NAME
, config
, ARRAYLEN(config
), 0);
420 rb
->memcpy(&old_settings
, &settings
, sizeof(settings
));
425 randomize_solution();
429 int button
= 0, guess
= 0, piece
= 0;
430 for (guess
= 0; guess
< guesses_count
&& !stop_game(); guess
++) {
431 while(!stop_game()) {
432 draw_board(guess
, piece
);
434 button
= get_button();
435 if (button
== PLA_SELECT
)
449 case PLA_RIGHT_REPEAT
:
450 piece
= (piece
+ 1) % pieces_count
;
455 case PLA_LEFT_REPEAT
:
456 piece
= (piece
+ pieces_count
- 1) % pieces_count
;
460 #ifdef HAVE_SCROLLWHEEL
462 case PLA_SCROLL_FWD_REPEAT
:
465 case PLA_DOWN_REPEAT
:
466 guesses
[guess
].pieces
[piece
] =
467 (guesses
[guess
].pieces
[piece
] + 1)
472 #ifdef HAVE_SCROLLWHEEL
473 case PLA_SCROLL_BACK
:
474 case PLA_SCROLL_BACK_REPEAT
:
478 guesses
[guess
].pieces
[piece
] =
479 (guesses
[guess
].pieces
[piece
] + colors_count
- 1)
484 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
488 if (guesses
[guess
].pieces
[piece
] == -1)
489 guesses
[guess
].pieces
[piece
] = 0;
493 validate_guess(&guesses
[guess
]);
495 if (guesses
[guess
].score
.correct
== pieces_count
)
498 if (guess
+ 1 < guesses_count
&& !found
)
499 guesses
[guess
+ 1] = guesses
[guess
];
505 if (!quit
&& !leave
) {
506 draw_board(guess
, piece
);
509 rb
->splash(HZ
, "Well done :)");
511 rb
->splash(HZ
, "Wooops :(");
513 button
= rb
->button_get(true);
514 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
517 } while( ( button
== BUTTON_NONE
)
518 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
522 if (rb
->memcmp(&old_settings
, &settings
, sizeof(settings
)))
523 configfile_save(CONFIG_FILE_NAME
, config
, ARRAYLEN(config
), 0);
525 rb
->lcd_setfont(FONT_UI
);
526 return (usb
) ? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;