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
;
92 static struct settings settings
= {
93 5, 7, 10, false, false,
95 static struct settings old_settings
;
96 static int pieces_count
;
97 static int colors_count
;
98 static int guesses_count
;
101 #define ALUMINIUM LCD_RGBPACK(136, 138, 133)
104 #define X_MARGIN (LCD_WIDTH / 20)
105 #define Y_MARGIN (LCD_HEIGHT / 20)
106 #define GAME_H (LCD_HEIGHT - (2 * Y_MARGIN))
107 #define LINE_W (LCD_WIDTH - (2 * X_MARGIN))
109 #define CONFIG_FILE_NAME "codebuster.cfg"
111 static struct configdata config
[] = {
112 {TYPE_INT
, 0, MAX_PIECES_COUNT
, { .int_p
= &settings
.pieces
}, "pieces", NULL
},
113 {TYPE_INT
, 0, MAX_COLORS_COUNT
, { .int_p
= &settings
.colors
}, "colors", NULL
},
114 {TYPE_INT
, 0, MAX_GUESSES_COUNT
, { .int_p
= &settings
.guesses
}, "guesses", NULL
},
115 {TYPE_BOOL
, 0, 1, { .bool_p
= &settings
.labeling
}, "labeling", NULL
},
116 {TYPE_BOOL
, 0, 1, { .bool_p
= &settings
.framing
}, "framing", NULL
},
120 static int piece_w
, tick_w
;
121 static int true_guess_w
, true_score_w
, guess_w
, score_w
;
123 /* Guesses and solution */
124 struct mm_line solution
, hidden
;
125 struct mm_line guesses
[MAX_GUESSES_COUNT
];
127 /* Alias for pluginlib_getaction */
128 static inline int get_button(void) {
129 return pluginlib_getaction(TIMEOUT_BLOCK
, plugin_contexts
, 2);
132 /* Computes the margin to center an element */
133 static inline int get_margin(int width
, int full_w
) {
134 return ((full_w
- width
) / 2);
137 static inline bool stop_game(void) {
138 return (quit
|| leave
|| found
);
141 static void fill_color_rect(int x
, int y
, int w
, int h
, int color
) {
142 rb
->lcd_set_foreground(color
);
143 rb
->lcd_fillrect(x
, y
, w
, h
);
144 rb
->lcd_set_foreground(LCD_WHITE
);
147 static void overfill_rect(int x
, int y
, int w
, int h
) {
148 rb
->lcd_fillrect(x
- 2, y
- 2, w
+ 4, h
+ 4);
151 static void draw_piece(int x
, int y
, int w
, int h
, int color_id
, bool emph
) {
152 int color
= LCD_BLACK
;
155 color
= colors
[color_id
];
156 else if (color_id
== -2) /* Hidden piece */
160 overfill_rect(x
, y
, w
, h
);
162 if (color_id
== -1) /* Uninitialised color */
163 rb
->lcd_drawrect(x
, y
, w
, h
);
165 fill_color_rect(x
, y
, w
, h
, color
);
167 if (!emph
&& settings
.framing
)
168 rb
->lcd_drawrect(x
, y
, w
, h
);
170 if (settings
.labeling
&& color_id
>= 0) {
172 rb
->snprintf(text
, 2, "%d", color_id
);
174 int fw
, fh
; rb
->font_getstringsize(text
, &fw
, &fh
, FONT_SYSFIXED
);
175 rb
->lcd_putsxy(x
+ get_margin(fw
, w
), y
+ get_margin(fh
, h
), text
);
179 /* Compute the score for a given guess (expressed in ticks) */
180 static void validate_guess(struct mm_line
* guess
) {
181 bool solution_match
[MAX_PIECES_COUNT
];
182 bool guess_match
[MAX_PIECES_COUNT
];
184 guess
->score
.misplaced
= 0;
185 guess
->score
.correct
= 0;
189 /* Initialisation with 0s */
190 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++)
191 solution_match
[guess_pos
] = guess_match
[guess_pos
] = false;
193 /* 1st step : detect correctly positioned pieces */
194 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
195 if (solution
.pieces
[guess_pos
] == guess
->pieces
[guess_pos
]) {
196 guess
->score
.correct
+= 1;
198 guess_match
[guess_pos
] = solution_match
[guess_pos
]
203 /* Second step : detect mispositioned pieces */
204 for (guess_pos
= 0; guess_pos
< pieces_count
; guess_pos
++) {
205 if (guess_match
[guess_pos
]) continue;
208 for (sol_pos
= 0; sol_pos
< pieces_count
; sol_pos
++) {
209 if (guess_match
[guess_pos
]) break;
210 if (solution_match
[sol_pos
]) continue;
212 if (guess
->pieces
[guess_pos
] == solution
.pieces
[sol_pos
]) {
213 guess
->score
.misplaced
+= 1;
215 solution_match
[sol_pos
] = true;
222 static void draw_guess(int line
, struct mm_line
* guess
, int cur_guess
,
223 int cur_piece
, bool show_score
) {
224 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
225 int l_margin
= X_MARGIN
+ (show_score
? 0 : get_margin(guess_w
, LINE_W
));
228 for (piece
= 0; piece
< pieces_count
; piece
++) {
229 int cur_x
= l_margin
+ 2 * piece_w
* piece
;
230 draw_piece(cur_x
, cur_y
, piece_w
, line_h
, guess
->pieces
[piece
],
231 line
== cur_guess
&& piece
== cur_piece
);
235 static void draw_score(int line
, struct mm_line
* guess
) {
236 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
237 int l_margin
= X_MARGIN
+ true_guess_w
+ MARGIN
;
240 for (; tick
< guess
->score
.correct
; 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(239, 41, 41));
246 for (; tick
< guess
->score
.correct
+ guess
->score
.misplaced
; 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(211, 215, 207));
253 static void draw_board(int cur_guess
, int cur_piece
) {
254 rb
->lcd_clear_display();
257 for (; line
< guesses_count
; line
++) {
258 draw_guess(line
, &guesses
[line
], cur_guess
, cur_piece
, true);
259 if (line
< cur_guess
) draw_score(line
, &guesses
[line
]);
263 int colors_margin
= 2;
264 int cur_y
= (Y_MARGIN
+ MARGIN
) + 2 * line_h
* line
;
265 int color_w
= (LINE_W
- colors_margin
* (colors_count
- 1)) / colors_count
;
267 for (color
= 0; color
< colors_count
; color
++) {
268 int cur_x
= X_MARGIN
+ color
* (color_w
+ colors_margin
);
269 draw_piece(cur_x
, cur_y
, color_w
, line_h
, color
,
270 color
== guesses
[cur_guess
].pieces
[cur_piece
]);
276 draw_guess(line
, &solution
, cur_guess
, cur_piece
, false);
278 draw_guess(line
, &hidden
, cur_guess
, cur_piece
, false);
283 static void init_vars(void) {
284 quit
= leave
= usb
= found
= game_ended
= false;
287 for (guess
= 0; guess
< guesses_count
; guess
++) {
288 for (piece
= 0; piece
< pieces_count
; piece
++)
289 guesses
[guess
].pieces
[piece
] = -1;
291 for (piece
= 0; piece
< pieces_count
; piece
++) {
292 guesses
[0].pieces
[piece
] = 0;
293 hidden
.pieces
[piece
] = -2;
297 static void init_board(void) {
299 pieces_count
= settings
.pieces
;
300 colors_count
= settings
.colors
;
301 guesses_count
= settings
.guesses
;
303 line_h
= GAME_H
/ (2 * (guesses_count
+ 2) - 1);
305 true_score_w
= LINE_W
* 0.25;
306 true_guess_w
= LINE_W
- (true_score_w
+ MARGIN
);
308 tick_w
= true_score_w
/ (2 * pieces_count
- 1);
309 piece_w
= true_guess_w
/ (2 * pieces_count
- 1);
311 /* Readjust (due to integer divisions) */
312 score_w
= tick_w
* (2 * pieces_count
- 1);
313 guess_w
= piece_w
* (2 * pieces_count
- 1);
316 static void randomize_solution(void) {
318 for (piece_id
= 0; piece_id
< pieces_count
; piece_id
++)
319 solution
.pieces
[piece_id
] = rb
->rand() % colors_count
;
322 static void settings_menu(void) {
323 MENUITEM_STRINGLIST(settings_menu
, "Settings", NULL
,
324 "Number of colours", "Number of pegs",
326 "Display labels", "Display frames");
328 bool menu_quit
= false;
332 switch(rb
->do_menu(&settings_menu
, &cur_item
, NULL
, false)) {
334 rb
->set_int("Number of colours", "", UNIT_INT
, &settings
.colors
,
335 NULL
, -1, MAX_COLORS_COUNT
, 1, NULL
);
338 rb
->set_int("Number of pegs", "", UNIT_INT
, &settings
.pieces
,
339 NULL
, -1, MAX_PIECES_COUNT
, 1, NULL
);
342 rb
->set_int("Number of guesses", "", UNIT_INT
, &settings
.guesses
,
343 NULL
, -1, MAX_GUESSES_COUNT
, 1, NULL
);
346 rb
->set_bool("Display labels", &settings
.labeling
);
349 rb
->set_bool("Display frames", &settings
.framing
);
361 static int menu_cb(int action
, const struct menu_item_ex
*this_item
)
363 int i
= ((intptr_t)this_item
);
364 if ((action
== ACTION_REQUEST_MENUITEM
) && (!resume
&& (i
==0)))
365 return ACTION_EXIT_MENUITEM
;
369 static void main_menu(void) {
370 MENUITEM_STRINGLIST(main_menu
, "Codebuster Menu", menu_cb
,
371 "Resume Game", "Start New Game", "Settings",
372 "Playback Control", "Quit");
374 bool menu_quit
= false;
378 switch(rb
->do_menu(&main_menu
, &cur_item
, NULL
, false)) {
391 playback_control(NULL
);
394 quit
= menu_quit
= true;
396 case MENU_ATTACHED_USB
:
397 usb
= menu_quit
= true;
405 enum plugin_status
plugin_start(const void* parameter
) {
408 rb
->srand(*rb
->current_tick
);
409 rb
->lcd_setfont(FONT_SYSFIXED
);
410 rb
->lcd_set_backdrop(NULL
);
411 rb
->lcd_set_foreground(LCD_WHITE
);
412 rb
->lcd_set_background(LCD_BLACK
);
414 configfile_load(CONFIG_FILE_NAME
, config
, ARRAYLEN(config
), 0);
415 rb
->memcpy(&old_settings
, &settings
, sizeof(settings
));
420 randomize_solution();
424 int button
= 0, guess
= 0, piece
= 0;
425 for (guess
= 0; guess
< guesses_count
&& !stop_game(); guess
++) {
426 while(!stop_game()) {
427 draw_board(guess
, piece
);
429 button
= get_button();
430 if (button
== PLA_FIRE
|| button
== PLA_START
)
444 case PLA_RIGHT_REPEAT
:
445 piece
= (piece
+ 1) % pieces_count
;
450 case PLA_LEFT_REPEAT
:
451 piece
= (piece
+ pieces_count
- 1) % pieces_count
;
456 case PLA_DOWN_REPEAT
:
457 guesses
[guess
].pieces
[piece
] =
458 (guesses
[guess
].pieces
[piece
] + 1)
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 (rb
->memcmp(&old_settings
, &settings
, sizeof(settings
)))
510 configfile_save(CONFIG_FILE_NAME
, config
, ARRAYLEN(config
), 0);
512 rb
->lcd_setfont(FONT_UI
);
513 return (usb
) ? PLUGIN_USB_CONNECTED
: PLUGIN_OK
;