A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd

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

+553
+1
apps/plugins/CATEGORIES
··· 14 14 chopper,games 15 15 clix,games 16 16 clock,apps 17 + codebuster,games 17 18 credits,viewers 18 19 cube,demos 19 20 demystify,demos
+1
apps/plugins/SOURCES
··· 67 67 #ifdef HAVE_LCD_COLOR 68 68 clix.c 69 69 ppmviewer.c 70 + codebuster.c 70 71 #endif 71 72 72 73 /* Plugins needing the grayscale lib on low-depth LCDs */
+514
apps/plugins/codebuster.c
··· 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 + 27 + PLUGIN_HEADER 28 + 29 + /* Limits */ 30 + #define MAX_PIECES_COUNT 5 31 + #define MAX_COLORS_COUNT 8 32 + #define MAX_GUESSES_COUNT 10 33 + 34 + const 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 + 68 + struct mm_score { 69 + int correct; 70 + int misplaced; 71 + }; 72 + 73 + struct mm_line { 74 + struct mm_score score; 75 + int pieces[MAX_PIECES_COUNT]; 76 + }; 77 + 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), 90 + }; 91 + 92 + /* Flags */ 93 + static bool quit, leave, usb; 94 + static bool found, game_ended; 95 + 96 + /* Settings */ 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; 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 + 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}, 122 + }; 123 + static bool settings_changed = false; 124 + 125 + static int line_h; 126 + static int piece_w, tick_w; 127 + static int true_guess_w, true_score_w, guess_w, score_w; 128 + 129 + /* Guesses and solution */ 130 + struct mm_line solution, hidden; 131 + struct mm_line guesses[MAX_GUESSES_COUNT]; 132 + 133 + /* Alias for pluginlib_getaction */ 134 + static inline int get_button(void) { 135 + return pluginlib_getaction(TIMEOUT_BLOCK, plugin_contexts, 2); 136 + } 137 + 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); 141 + } 142 + 143 + static inline bool stop_game(void) { 144 + return (quit || leave || found); 145 + } 146 + 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); 151 + } 152 + 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); 155 + } 156 + 157 + static 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) */ 186 + static 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 + 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)); 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 + 241 + static 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 + 259 + static 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 + 289 + static 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 + 303 + static 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 + 322 + static 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 + 328 + static 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 + 366 + static bool resume; 367 + static 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 + 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"); 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 + 413 + enum 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 + }
+1
manual/plugins/brickmania.tex
··· 1 + % $Id: 1 2 \subsection{BrickMania} 2 3 \screenshot{plugins/images/ss-brickmania}{BrickMania}% 3 4 {img:brickmania}
+34
manual/plugins/codebuster.tex
··· 1 + % $Id: 2 + \subsection{Codebuster} 3 + \screenshot{plugins/images/ss-codebuster}{Codebuster}{img:codebuster} 4 + 5 + Codebuster is a clone of the classic mastermind game. Your aim is to guess the 6 + computer's combination in a minimal number of attempts. At every turn, you input 7 + a combination, which is then validated by the computer, which displays the number 8 + of well-placed and misplaced pegs (a red square for right ones, and a white square 9 + for misplaced ones). 10 + 11 + \begin{table} 12 + \begin{btnmap}{}{} 13 + \opt{IAUDIO_X5_PAD,SANSA_E200_PAD,SANSA_C200_PAD,GIGABEAT_PAD}{\ButtonPower} 14 + \opt{SANSA_FUZE_PAD}{Long \ButtonHome} 15 + \opt{IPOD_4G_PAD}{\ButtonMenu} 16 + \opt{IRIVER_H300_PAD}{\ButtonOff} 17 + \opt{GIGABEAT_S_PAD}{\ButtonBack} 18 + \opt{HAVEREMOTEKEYMAP}{& } 19 + & Show menu \\ 20 + \opt{IRIVER_H300_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,SANSA_E200_PAD,SANSA_C200_PAD,SANSA_FUZE_PAD,IPOD_4G_PAD} 21 + {\ButtonSelect} 22 + \opt{IAUDIO_X5_PAD}{\ButtonRec} 23 + \opt{HAVEREMOTEKEYMAP}{& } 24 + & Validate suggestion and move to next line \\ 25 + \ButtonLeft\ / \ButtonRight 26 + \opt{HAVEREMOTEKEYMAP}{& } 27 + & Select a peg \\ 28 + \opt{IRIVER_H300_PAD,IAUDIO_X5_PAD,GIGABEAT_PAD,GIGABEAT_S_PAD,SANSA_C200_PAD} 29 + {\ButtonUp\ / \ButtonDown} 30 + \opt{SANSA_E200_PAD,SANSA_FUZE_PAD,IPOD_4G_PAD}{\ButtonScrollFwd\ / \ButtonScrollBack} 31 + \opt{HAVEREMOTEKEYMAP}{& } 32 + & Change current peg \\ 33 + \end{btnmap} 34 + \end{table}
manual/plugins/images/ss-codebuster-132x80x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-160x128x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-176x132x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-176x220x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-220x176x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-240x320x16.png

This is a binary file and will not be displayed.

manual/plugins/images/ss-codebuster-320x240x16.png

This is a binary file and will not be displayed.

+2
manual/plugins/main.tex
··· 31 31 32 32 \opt{lcd_bitmap}{\input{plugins/chopper.tex}} 33 33 34 + \opt{lcd_color}{\input{plugins/codebuster.tex}} 35 + 34 36 {\input{plugins/dice.tex}} 35 37 36 38 \opt{swcodec}{\input{plugins/doom.tex}}