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 * * (guesses_count) lines of guesses,
40 * * 1 center line of solution (hidden),
41 * * 1 line showing available colors.
44 * * quit: exit the plugin
45 * * leave: restart the plugin (leave the current game)
46 * * game_ended: the game has ended
47 * * found: the combination has been found
49 * Colors used are taken from the Tango project.
51 * Due to integer truncations, 2 vars are used for some objects' dimensions
52 * (eg. true_guess_w, true_score_w). The actual dimension of these objects is
53 * stored in the corresponding var. without the "true" prefix.
62 struct mm_score score
;
63 int pieces
[MAX_PIECES_COUNT
];
66 const int colors
[MAX_COLORS_COUNT
] = {
67 LCD_RGBPACK(252, 233, 79),
68 LCD_RGBPACK(206, 92, 0),
69 LCD_RGBPACK(143, 89, 2),
70 LCD_RGBPACK( 78, 154, 6),
71 /* LCD_RGBPACK( 32, 74, 135), */
72 LCD_RGBPACK( 52, 101, 164),
73 /* LCD_RGBPACK(114, 159, 207), */
74 LCD_RGBPACK(117, 80, 123),
75 /* LCD_RGBPACK(173, 127, 168), */
76 LCD_RGBPACK(164, 0, 0),
77 LCD_RGBPACK(238, 238, 236),
81 static bool quit
, leave
, usb
;
82 static bool found
, game_ended
;
85 static int pieces_count
;
86 static int colors_count
;
87 static int guesses_count
;
88 static int pieces_tmp
= 5;
89 static int colors_tmp
= 7;
90 static int guesses_tmp
= 10;
91 static bool labeling
= false, framing
= false;
94 #define ALUMINIUM LCD_RGBPACK(136, 138, 133)
97 #define X_MARGIN (LCD_WIDTH / 20)
98 #define Y_MARGIN (LCD_HEIGHT / 20)
99 #define GAME_H (LCD_HEIGHT - (2 * Y_MARGIN))
100 #define LINE_W (LCD_WIDTH - (2 * X_MARGIN))
102 #define CONFIG_FILE_NAME "codebuster.cfg"
104 static struct configdata config
[] = {
105 {TYPE_INT
, 0, MAX_PIECES_COUNT
, { .int_p
= &pieces_tmp
}, "pieces", NULL
},
106 {TYPE_INT
, 0, MAX_COLORS_COUNT
, { .int_p
= &colors_tmp
}, "colors", NULL
},
107 {TYPE_INT
, 0, MAX_GUESSES_COUNT
, { .int_p
= &guesses_tmp
}, "guesses", NULL
},
108 {TYPE_BOOL
, 0, 1, { .bool_p
= &labeling
}, "labeling", NULL
},
109 {TYPE_BOOL
, 0, 1, { .bool_p
= &framing
}, "framing", NULL
},
111 static bool settings_changed
= false;
114 static int piece_w
, tick_w
;
115 static int true_guess_w
, true_score_w
, guess_w
, score_w
;
117 /* Guesses and solution */
118 struct mm_line solution
, hidden
;
119 struct mm_line guesses
[MAX_GUESSES_COUNT
];
121 /* Alias for pluginlib_getaction */
122 static inline int get_button(void) {
123 return pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, 2);
126 /* Computes the margin to center an element */
127 static inline int get_margin(int width
, int full_w
) {
128 return ((full_w
- width
) / 2);
131 static inline bool stop_game(void) {
132 return (quit
|| leave
|| found
);
135 static void fill_color_rect(int x
, int y
, int w
, int h
, int color
) {
136 rb
->lcd_set_foreground(color
);
137 rb
->lcd_fillrect(x
, y
, w
, h
);
138 rb
->lcd_set_foreground(LCD_WHITE
);
141 static void overfill_rect(int x
, int y
, int w
, int h
) {
142 rb
->lcd_fillrect(x
- 2, y
- 2, w
+ 4, h
+ 4);
145 static void draw_piece(int x
, int y
, int w
, int h
, int color_id
, bool emph
) {
146 int color
= LCD_BLACK
;
149 color
= colors
[color_id
];
150 else if (color_id
== -2) /* Hidden piece */
154 overfill_rect(x
, y
, w
, h
);
156 if (color_id
== -1) /* Uninitialised color */
157 rb
->lcd_drawrect(x
, y
, w
, h
);
159 fill_color_rect(x
, y
, w
, h
, color
);
161 if (!emph
&& framing
)
162 rb
->lcd_drawrect(x
, y
, w
, h
);
164 if (labeling
&& color_id
>= 0) {
166 rb
->snprintf(text
, 2, "%d", color_id
);
168 int fw
, fh
; rb
->font_getstringsize(text
, &fw
, &fh
, FONT_SYSFIXED
);
169 rb
->lcd_putsxy(x
+ get_margin(fw
, w
), y
+ get_margin(fh
, h
), text
);
173 /* Compute the score for a given guess (expressed in ticks) */
174 static void validate_guess(struct mm_line
* guess
) {
175 bool solution_match
[pieces_count
];
176 bool guess_match
[pieces_count
];
178 guess
->score
.misplaced
= 0;
179 guess
->score
.correct
= 0;
183 /* Initialisation with 0s */
184 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++)
185 solution_match
[guess_pos
] = guess_match
[guess_pos
] = false;
187 /* 1st step : detect correctly positioned pieces */
188 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
189 if (solution
.pieces
[guess_pos
] == guess
->pieces
[guess_pos
]) {
190 guess
->score
.correct
+= 1;
192 guess_match
[guess_pos
] = solution_match
[guess_pos
]
197 /* Second step : detect mispositioned pieces */
198 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
199 if (guess_match
[guess_pos
]) continue;
202 for (sol_pos
= 0; sol_pos
< pieces_count
; sol_pos
++) {
203 if (guess_match
[guess_pos
]) break;
204 if (solution_match
[sol_pos
]) continue;
206 if (guess
->pieces
[guess_pos
] == solution
.pieces
[sol_pos
]) {
207 guess
->score
.misplaced
+= 1;
209 solution_match
[sol_pos
] = true;
216 static void draw_guess(int line
, struct mm_line
* guess
, int cur_guess
,
217 int cur_piece
, bool show_score
) {
218 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
219 int l_margin
= X_MARGIN
+ (show_score
? 0 : get_margin(guess_w
, LINE_W
));
222 for (piece
= 0; piece
< pieces_count
; piece
++) {
223 int cur_x
= l_margin
+ 2 * piece_w
* piece
;
224 draw_piece(cur_x
, cur_y
, piece_w
, line_h
, guess
->pieces
[piece
],
225 line
== cur_guess
&& piece
== cur_piece
);
229 static void draw_score(int line
, struct mm_line
* guess
) {
230 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
231 int l_margin
= X_MARGIN
+ true_guess_w
+ MARGIN
;
234 for (; tick
< guess
->score
.correct
; tick
++) {
235 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
237 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(239, 41, 41));
240 for (; tick
< guess
->score
.correct
+ guess
->score
.misplaced
; tick
++) {
241 int cur_x
= l_margin
+ 2 * tick_w
* tick
;
243 fill_color_rect(cur_x
, cur_y
, tick_w
, line_h
, LCD_RGBPACK(211, 215, 207));
247 static void draw_board(int cur_guess
, int cur_piece
) {
248 rb
->lcd_clear_display();
251 for (; line
< guesses_count
; line
++) {
252 draw_guess(line
, &guesses
[line
], cur_guess
, cur_piece
, true);
253 if (line
< cur_guess
) draw_score(line
, &guesses
[line
]);
257 int colors_margin
= 2;
258 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
259 int color_w
= (LINE_W
- colors_margin
* (colors_count
- 1)) / colors_count
;
261 for (color
= 0; color
< colors_count
; color
++) {
262 int cur_x
= X_MARGIN
+ color
* (color_w
+ colors_margin
);
263 draw_piece(cur_x
, cur_y
, color_w
, line_h
, color
,
264 color
== guesses
[cur_guess
].pieces
[cur_piece
]);
270 draw_guess(line
, &solution
, cur_guess
, cur_piece
, false);
272 draw_guess(line
, &hidden
, cur_guess
, cur_piece
, false);
277 static void init_vars(void) {
278 quit
= leave
= usb
= found
= game_ended
= false;
281 for (guess
= 0; guess
< guesses_count
; guess
++) {
282 for (piece
= 0; piece
< pieces_count
; piece
++)
283 guesses
[guess
].pieces
[piece
] = -1;
285 for (piece
= 0; piece
< pieces_count
; piece
++) {
286 guesses
[0].pieces
[piece
] = 0;
287 hidden
.pieces
[piece
] = -2;
291 static void init_board(void) {
293 pieces_count
= pieces_tmp
;
294 colors_count
= colors_tmp
;
295 guesses_count
= guesses_tmp
;
297 line_h
= GAME_H
/ (2 * (guesses_count
+ 2) - 1);
299 true_score_w
= LINE_W
* 0.25;
300 true_guess_w
= LINE_W
- (true_score_w
+ MARGIN
);
302 tick_w
= true_score_w
/ (2 * pieces_count
- 1);
303 piece_w
= true_guess_w
/ (2 * pieces_count
- 1);
305 /* Readjust (due to integer divisions) */
306 score_w
= tick_w
* (2 * pieces_count
- 1);
307 guess_w
= piece_w
* (2 * pieces_count
- 1);
310 static void randomize_solution(void) {
312 for (piece_id
= 0; piece_id
< pieces_count
; piece_id
++)
313 solution
.pieces
[piece_id
] = rb
->rand() % colors_count
;
316 static void settings_menu(void) {
317 MENUITEM_STRINGLIST(settings_menu
, "Settings", NULL
,
318 "Number of colours", "Number of pegs",
319 "Number of guesses", "Labels", "Frames");
323 bool menu_quit
= false;
326 switch(rb
->do_menu(&settings_menu
, &cur_item
, NULL
, false)) {
328 rb
->set_int("Number of colours", "", UNIT_INT
, &colors_tmp
,
329 NULL
, -1, MAX_COLORS_COUNT
, 1, NULL
);
332 rb
->set_int("Number of pegs", "", UNIT_INT
, &pieces_tmp
,
333 NULL
, -1, MAX_PIECES_COUNT
, 1, NULL
);
336 rb
->set_int("Number of guesses", "", UNIT_INT
, &guesses_tmp
,
337 NULL
, -1, MAX_GUESSES_COUNT
, 1, NULL
);
340 rb
->set_bool("Display labels", &labeling
);
343 rb
->set_bool("Display frames", &framing
);
355 static int menu_cb(int action
, const struct menu_item_ex
*this_item
)
357 int i
= ((intptr_t)this_item
);
358 if ((action
== ACTION_REQUEST_MENUITEM
) && (!resume
&& (i
==0)))
359 return ACTION_EXIT_MENUITEM
;
363 static void main_menu(void) {
364 MENUITEM_STRINGLIST(main_menu
, "Codebuster Menu", menu_cb
,
365 "Resume Game", "Start New Game", "Settings",
366 "Playback Control", "Quit");
370 bool menu_quit
= false;
373 switch(rb
->do_menu(&main_menu
, &cur_item
, NULL
, false)) {
384 settings_changed
= true;
387 playback_control(NULL
);
390 quit
= menu_quit
= true;
392 case MENU_ATTACHED_USB
:
393 usb
= menu_quit
= true;
401 enum plugin_status
plugin_start(const void* parameter
) {
404 rb
->srand(*rb
->current_tick
);
405 rb
->lcd_setfont(FONT_SYSFIXED
);
406 rb
->lcd_set_backdrop(NULL
);
407 rb
->lcd_set_foreground(LCD_WHITE
);
408 rb
->lcd_set_background(LCD_BLACK
);
410 configfile_load(CONFIG_FILE_NAME
,config
,5,0);
415 randomize_solution();
419 int button
= 0, guess
= 0, piece
= 0;
420 for (guess
= 0; guess
< guesses_count
&& !stop_game(); guess
++) {
421 while(!stop_game()) {
422 draw_board(guess
, piece
);
424 button
= get_button();
425 if (button
== PLA_FIRE
|| button
== PLA_START
)
439 case PLA_RIGHT_REPEAT
:
440 piece
= (piece
+ 1) % pieces_count
;
445 case PLA_LEFT_REPEAT
:
446 piece
= (piece
+ pieces_count
- 1) % pieces_count
;
451 case PLA_DOWN_REPEAT
:
452 guesses
[guess
].pieces
[piece
] =
453 (guesses
[guess
].pieces
[piece
] + 1)
460 guesses
[guess
].pieces
[piece
] =
461 (guesses
[guess
].pieces
[piece
] + colors_count
- 1)
466 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
)
470 if (guesses
[guess
].pieces
[piece
] == -1)
471 guesses
[guess
].pieces
[piece
] = 0;
475 validate_guess(&guesses
[guess
]);
477 if (guesses
[guess
].score
.correct
== pieces_count
)
480 if (guess
+ 1 < guesses_count
&& !found
)
481 guesses
[guess
+ 1] = guesses
[guess
];
487 if (!quit
&& !leave
) {
488 draw_board(guess
, piece
);
491 rb
->splash(HZ
, "Well done :)");
493 rb
->splash(HZ
, "Wooops :(");
495 button
= rb
->button_get(true);
496 if (rb
->default_event_handler(button
) == SYS_USB_CONNECTED
) {
499 } while( ( button
== BUTTON_NONE
)
500 || ( button
& (BUTTON_REL
|BUTTON_REPEAT
) ) );
504 if (settings_changed
)
505 configfile_save(CONFIG_FILE_NAME
,config
,5,0);
507 rb
->lcd_setfont(FONT_UI
);
508 return (usb
) ? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;