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
[] =
35 {generic_directions
, generic_actions
};
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
51 * * (guesses_count) lines of guesses,
52 * * 1 center line of solution (hidden),
53 * * 1 line showing available colors.
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
61 * Colors used are taken from the Tango project.
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.
74 struct mm_score score
;
75 int pieces
[MAX_PIECES_COUNT
];
78 const 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),
93 static bool quit
, leave
, usb
;
94 static bool found
, game_ended
;
97 static int pieces_count
;
98 static int colors_count
;
99 static int guesses_count
;
100 static int pieces_tmp
= 5;
101 static int colors_tmp
= 7;
102 static int guesses_tmp
= 10;
103 static bool labeling
= false, framing
= false;
106 #define ALUMINIUM LCD_RGBPACK(136, 138, 133)
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))
114 #define CONFIG_FILE_NAME "codebuster.cfg"
116 static 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
},
123 static bool settings_changed
= false;
126 static int piece_w
, tick_w
;
127 static int true_guess_w
, true_score_w
, guess_w
, score_w
;
129 /* Guesses and solution */
130 struct mm_line solution
, hidden
;
131 struct mm_line guesses
[MAX_GUESSES_COUNT
];
133 /* Alias for pluginlib_getaction */
134 static inline int get_button(void) {
135 return pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, 2);
138 /* Computes the margin to center an element */
139 static inline int get_margin(int width
, int full_w
) {
140 return ((full_w
- width
) / 2);
143 static inline bool stop_game(void) {
144 return (quit
|| leave
|| found
);
147 static 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
);
153 static void overfill_rect(int x
, int y
, int w
, int h
) {
154 rb
->lcd_fillrect(x
- 2, y
- 2, w
+ 4, h
+ 4);
157 static void draw_piece(int x
, int y
, int w
, int h
, int color_id
, bool emph
) {
158 int color
= LCD_BLACK
;
161 color
= colors
[color_id
];
162 else if (color_id
== -2) /* Hidden piece */
166 overfill_rect(x
, y
, w
, h
);
168 if (color_id
== -1) /* Uninitialised color */
169 rb
->lcd_drawrect(x
, y
, w
, h
);
171 fill_color_rect(x
, y
, w
, h
, color
);
173 if (!emph
&& framing
)
174 rb
->lcd_drawrect(x
, y
, w
, h
);
176 if (labeling
&& color_id
>= 0) {
178 rb
->snprintf(text
, 2, "%d", color_id
);
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
);
185 /* Compute the score for a given guess (expressed in ticks) */
186 static void validate_guess(struct mm_line
* guess
) {
187 bool solution_match
[pieces_count
];
188 bool guess_match
[pieces_count
];
190 guess
->score
.misplaced
= 0;
191 guess
->score
.correct
= 0;
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;
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;
204 guess_match
[guess_pos
] = solution_match
[guess_pos
]
209 /* Second step : detect mispositioned pieces */
210 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
211 if (guess_match
[guess_pos
]) continue;
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;
218 if (guess
->pieces
[guess_pos
] == solution
.pieces
[sol_pos
]) {
219 guess
->score
.misplaced
+= 1;
221 solution_match
[sol_pos
] = true;
228 static 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
));
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
);
241 static void draw_score(int line
, struct mm_line
* guess
) {
242 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
243 int l_margin
= X_MARGIN
+ true_guess_w
+ MARGIN
;
246 for (; tick
< guess
->score
.correct
; tick
++) {
247 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
249 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(239, 41, 41));
252 for (; tick
< guess
->score
.correct
+ guess
->score
.misplaced
; tick
++) {
253 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
255 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(211, 215, 207));
259 static void draw_board(int cur_guess
, int cur_piece
) {
260 rb
->lcd_clear_display();
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
]);
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
;
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
]);
282 draw_guess(line
, &solution
, cur_guess
, cur_piece
, false);
284 draw_guess(line
, &hidden
, cur_guess
, cur_piece
, false);
289 static void init_vars(void) {
290 quit
= leave
= usb
= found
= game_ended
= false;
293 for (guess
= 0; guess
< guesses_count
; guess
++) {
294 for (piece
= 0; piece
< pieces_count
; piece
++)
295 guesses
[guess
].pieces
[piece
] = -1;
297 for (piece
= 0; piece
< pieces_count
; piece
++) {
298 guesses
[0].pieces
[piece
] = 0;
299 hidden
.pieces
[piece
] = -2;
303 static void init_board(void) {
305 pieces_count
= pieces_tmp
;
306 colors_count
= colors_tmp
;
307 guesses_count
= guesses_tmp
;
309 line_h
= GAME_H
/ (2 * (guesses_count
+ 2) - 1);
311 true_score_w
= LINE_W
* 0.25;
312 true_guess_w
= LINE_W
- (true_score_w
+ MARGIN
);
314 tick_w
= true_score_w
/ (2 * pieces_count
- 1);
315 piece_w
= true_guess_w
/ (2 * pieces_count
- 1);
317 /* Readjust (due to integer divisions) */
318 score_w
= tick_w
* (2 * pieces_count
- 1);
319 guess_w
= piece_w
* (2 * pieces_count
- 1);
322 static void randomize_solution(void) {
324 for (piece_id
= 0; piece_id
< pieces_count
; piece_id
++)
325 solution
.pieces
[piece_id
] = rb
->rand() % colors_count
;
328 static void settings_menu(void) {
329 MENUITEM_STRINGLIST(settings_menu
, "Settings", NULL
,
330 "Number of colours", "Number of pegs",
331 "Number of guesses", "Labels", "Frames");
335 bool menu_quit
= false;
338 switch(rb
->do_menu(&settings_menu
, &cur_item
, NULL
, false)) {
340 rb
->set_int("Number of colours", "", UNIT_INT
, &colors_tmp
,
341 NULL
, -1, MAX_COLORS_COUNT
, 1, NULL
);
344 rb
->set_int("Number of pegs", "", UNIT_INT
, &pieces_tmp
,
345 NULL
, -1, MAX_PIECES_COUNT
, 1, NULL
);
348 rb
->set_int("Number of guesses", "", UNIT_INT
, &guesses_tmp
,
349 NULL
, -1, MAX_GUESSES_COUNT
, 1, NULL
);
352 rb
->set_bool("Display labels", &labeling
);
355 rb
->set_bool("Display frames", &framing
);
367 static int menu_cb(int action
, const struct menu_item_ex
*this_item
)
369 int i
= ((intptr_t)this_item
);
370 if ((action
== ACTION_REQUEST_MENUITEM
) && (!resume
&& (i
==0)))
371 return ACTION_EXIT_MENUITEM
;
375 static void main_menu(void) {
376 MENUITEM_STRINGLIST(main_menu
, "Codebuster Menu", menu_cb
,
377 "Resume Game", "Start New Game", "Settings",
378 "Playback Control", "Quit");
382 bool menu_quit
= false;
385 switch(rb
->do_menu(&main_menu
, &cur_item
, NULL
, false)) {
396 settings_changed
= true;
399 playback_control(NULL
);
402 quit
= menu_quit
= true;
404 case MENU_ATTACHED_USB
:
405 usb
= menu_quit
= true;
413 enum plugin_status
plugin_start(const void* parameter
) {
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
);
422 configfile_load(CONFIG_FILE_NAME
,config
,5,0);
427 randomize_solution();
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
);
436 if ((button
= get_button()) == VALIDATE
) break;
446 case NEXT_PIECE_REPEAT
:
447 piece
= (piece
+ 1) % pieces_count
;
451 case PREV_PIECE_REPEAT
:
452 piece
= (piece
+ pieces_count
- 1) % pieces_count
;
457 case NEXT_COLOR_REPEAT
:
458 guesses
[guess
].pieces
[piece
] =
459 (guesses
[guess
].pieces
[piece
] + 1)
464 case PREV_COLOR_REPEAT
:
465 guesses
[guess
].pieces
[piece
] =
466 (guesses
[guess
].pieces
[piece
] + colors_count
- 1)
471 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
475 if (guesses
[guess
].pieces
[piece
] == -1)
476 guesses
[guess
].pieces
[piece
] = 0;
480 validate_guess(&guesses
[guess
]);
482 if (guesses
[guess
].score
.correct
== pieces_count
)
485 if (guess
+ 1 < guesses_count
&& !found
)
486 guesses
[guess
+ 1] = guesses
[guess
];
492 if (!quit
&& !leave
) {
493 draw_board(guess
, piece
);
496 rb
->splash(HZ
, "Well done :)");
498 rb
->splash(HZ
, "Wooops :(");
500 button
= rb
->button_get(true);
501 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
504 } while( ( button
== BUTTON_NONE
)
505 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
509 if (settings_changed
)
510 configfile_save(CONFIG_FILE_NAME
,config
,5,0);
512 rb
->lcd_setfont(FONT_UI
);
513 return (usb
) ? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;