#include "plugin.h" #include "lib/playback_control.h" //#define DEBUG #define MAX_SIZE 20 #define N_CELLS SIZE*SIZE #define MAX_BOARD_SIZE MAX_SIZE*MAX_SIZE #define CELL_SIZE ((LCD_HEIGHT0)?x:x*-1) #endif #define FOREACH_PIXEL(ptr) for(ptr=board;ptr> 6) #define GET_STATUS(x, y) GET_STATUS_PTR(BOARD_PTR(x, y)) #define SET_STATUS_PTR(ptr, status) *ptr = GET_VALUE_PTR(ptr)+(status<<6) #define SET_STATUS(x, y, status) SET_STATUS_PTR(BOARD_PTR(x, y), status) enum status { INTACT, //The pixel has never changed its state SELECTED, //The pixel is adjacent to the main area, but not part of it FILLED, //The pixel is in the same area as the top left one NEWLY_FILLED //The pixel is in the same area as the top left one since last move }; static unsigned colors[N_COLOURS] = { LCD_RGBPACK(255, 119, 34), LCD_RGBPACK(255, 255, 102), LCD_RGBPACK(119, 204, 51), LCD_RGBPACK(102, 170, 255), LCD_RGBPACK(51, 68, 255), LCD_RGBPACK(51, 51, 51) }; static int SIZE = 15; static unsigned char hover_color = 0; static unsigned char prev_color = 0; static unsigned char cur_color = 0xff; static unsigned char board[MAX_BOARD_SIZE]={0}; static unsigned char n_moves = 0; static unsigned char par = 0; bool fillrect_radius (int x, int y, int w, int h, int radius) { int i; rb->lcd_fillrect(x, y+radius, w, h-2*radius); for (i=0; ilcd_hline(x+radius-l, x+w-radius+l, y+radius-i); } return true; } //The menu function is defined at the end bool menu(void); #ifndef DEBUG /*Draws the main table*/ void draw_board(void) { int x, y; unsigned color; for (x=0; xlcd_set_foreground (colors[color]); rb->lcd_fillrect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE,CELL_SIZE); } } } #else void draw_board(void) { int x, y; unsigned color; for (x=0; xlcd_set_foreground (colors[color]); rb->lcd_fillrect(x*CELL_SIZE, y*CELL_SIZE, CELL_SIZE,CELL_SIZE); rb->lcd_set_foreground (LCD_RGBPACK( (GET_STATUS(x,y)==SELECTED)?200:0, 0, (GET_STATUS(x,y)==NEWLY_FILLED)?200:0)); rb->lcd_fillrect((x+0.5) * CELL_SIZE - 2, (y+0.5) * CELL_SIZE - 2, 4,4); } } } #endif /*Draws the panel used to select a colour*/ void draw_color_chooser (void){ int x,y, width, height; int addx, addy, box_width, box_height; int infobox; int i; if (LCD_WIDTHlcd_set_foreground(colors[i]); rb->lcd_fillrect(x+i*addx, y+i*addy, box_width, box_height); if (hover_color==i) { rb->lcd_set_foreground(LCD_RGBPACK(10,10,10)); rb->lcd_fillrect((x+i*addx)+(box_width/2)-3, y+i*addy+box_height/2-3, 6, 6); rb->lcd_set_foreground(LCD_RGBPACK(100,100,100)); rb->lcd_drawrect((x+i*addx)+(box_width/2)-3, y+i*addy+box_height/2-3, 6, 6); } } rb->lcd_set_foreground(LCD_RGBPACK(5,5,5)); rb->lcd_fillrect(x+i*addx, y+i*addy, infobox, infobox); rb->lcd_set_foreground(LCD_RGBPACK(100,100,100)); rb->lcd_putsxyf(x+i*addx, y+i*addy, "Moves : %d", n_moves); i++; rb->lcd_putsxyf(x+i*addx, y+i*addy, "Par : %d", par); } /*Draws the GUI*/ void draw_screen(void){ rb->lcd_set_foreground(LCD_RGBPACK(0,0,0)); rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT); draw_board(); draw_color_chooser(); rb->lcd_update(); } void dump_board(void) { rb->splashf(HZ/2, "%d %d %d %d", BOARD(0,0), BOARD(1,0), BOARD(0,1), BOARD(1,1)); } inline void select_regions(int x, int y, unsigned char color);//Needed for select_region /*Add the SELECTED status to all pixels in the same area as (x, y) and which color is color. An human should just set color to 0xff. So that all pixels in the area are selected*/ void select_region (int x, int y, unsigned char color) { if(x<0 || y<0 || x>=SIZE || y>=SIZE) return;//Out of the board if (color == 0xff) color = GET_VALUE(x, y); if (GET_VALUE(x,y) != color /*Not Matching*/ || GET_STATUS(x,y) == FILLED || GET_STATUS(x, y) == NEWLY_FILLED /*Already filled*/ || GET_STATUS(x,y) == SELECTED /*Already selected*/ ) return; SET_STATUS(x, y, SELECTED); select_region(x, y+1, color); select_region(x, y-1, color); select_region(x+1, y, color); select_region(x-1, y, color); } /*Call select_region for each cell adjacent to (x, y)*/ inline void select_regions(int x, int y, unsigned char color){ select_region(x, y+1, color); select_region(x, y-1, color); select_region(x+1, y, color); select_region(x-1, y, color); } /*Returns the number of pixels with the SELECTED status - color is the color of the pixels to count. Set color to 0xff to count all the selected pixels */ int count_selected (unsigned char color) { unsigned char *pixel=NULL; int n=0; FOREACH_PIXEL(pixel){ if(GET_STATUS_PTR(pixel)==SELECTED){ if (color==0xff || GET_VALUE_PTR(pixel)==color) n++; } } return n; } /*Fills the *colors array, which must be at least N_COLOURS long, with the number of pixels set by each color*/ void count_selected_all (unsigned char *colors_ptr) { unsigned char *pixel=NULL; unsigned char i; for (i=0; i<=N_COLOURS; i++){ colors_ptr[i]=0; } FOREACH_PIXEL(pixel){ if(GET_STATUS_PTR(pixel)==SELECTED){ colors_ptr[N_COLOURS] ++; colors_ptr[GET_VALUE_PTR(pixel)] ++; } } } bool change_color (unsigned char color) { int x, y; if (color==cur_color || count_selected (color)==0) return false; for (x=0; xsplashf(HZ*2, "You can do better... You used %d more moves than the computer", ABS(dif)); }else if(dif==0){ rb->splashf(HZ*2, "You used as many moves as the computer. Not bad!"); }else{ rb->splashf(HZ*2, "Wow! You used %d less moves than the computer. You won!", ABS(dif)); } return true; } return false; } /*Call this function if the board was messed up. It selects all the pixels bounding the main area. But it DEOESN'T remove the current selections*/ void redo_selections(void) { int x, y; /*Select all the pixels bounding the main area*/ for (x=0; x 0) { moves++; best_count = 0; for (color=0; colorbest_count) { best_count = count; best_color = color; } } if (!change_color(best_color)) return 0; } restore_board(); return moves; } int solve_board_clever (void){ unsigned char color, best_color=0; int best_count, count; int moves=0; unsigned char all_colors_count [N_COLOURS+1]; while (count_selected(0xff) > 0) { moves++; best_count = 0; count_selected_all (all_colors_count); for (color=0; colorbest_count || count==0) {//count==0 <=> We won best_count = count; best_color = color; } n_moves++;//Else, undo_move will refuse processing undo_move(); } } if (!change_color(best_color)) return 0; } restore_board(); return moves; } void init_board(void) { int i; n_moves = 0; rb->srand(*rb->current_tick); for (i=0; irand()%N_COLOURS; } restore_board(); //rb->splashf(HZ/3, "Calculating par..."); par = solve_board_clever(); } bool menu (void) { bool menu_quit = false; int selection = 0; MENUITEM_STRINGLIST( menu, "Pixel-painter", NULL, "Resume", "Start new game", "Grid size", "Playback Control", "Quit" ); while( !menu_quit ) { switch( rb->do_menu( &menu, &selection, NULL, false ) ) { case 0: menu_quit = true; break; case 1: init_board(); menu_quit = true; break; case 2: rb->set_int( "Number of pixels per row", "", UNIT_INT, &SIZE, NULL, 1, 5, MAX_SIZE, NULL ); init_board(); menu_quit = true; break; case 3: playback_control( NULL ); break; default: return false; } } return true; } enum plugin_status wait_actions (void) { int button, quit = 0; bool real_action; while(!quit) { real_action = false; button=rb->get_action(CONTEXT_STD, 1); switch(button) { case ACTION_STD_NEXTREPEAT: case ACTION_STD_NEXT: //dump_board(); if (hover_color0)hover_color --; real_action=true; break; case ACTION_STD_CANCEL: undo_move(); real_action=true; break; case ACTION_STD_OK: if (change_color(hover_color)) { n_moves++; if (check_won()){ if (menu() == false) quit = true; else real_action = true; } }else{ rb->splashf(HZ/2, "Illegal Move !"); } real_action=true; break; case ACTION_STD_MENU: if (menu() == false) quit = true; else real_action = true; break; default: if (rb->default_event_handler(button) == SYS_USB_CONNECTED) { return PLUGIN_USB_CONNECTED; } break; } if (real_action) draw_screen(); } return PLUGIN_OK; } enum plugin_status plugin_start(const void* parameter) { (void)parameter; #ifdef DEBUG SIZE = 2; #endif init_board(); draw_board(); draw_color_chooser(); rb->lcd_update (); return wait_actions(); }