From bc00d38987a99eda1e38f2db62ebafb94cb9d6a5 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Thu, 28 Aug 2014 19:19:08 -0400 Subject: [WIP] Superdom: game improvements - macro-ify board dimensions, surrender thresholds - make AI skill level adjustable - let AI buy nukes in hard mode (still can't use them yet) - make nukes persistent (stay after an invasion) - make AI treat nukes with priority Change-Id: I1add6250766810787080624bd9e36026df449509 Reviewed-on: http://gerrit.rockbox.org/940 Reviewed-by: Michael Giacomelli --- apps/plugins/superdom.c | 310 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 212 insertions(+), 98 deletions(-) (limited to 'apps/plugins/superdom.c') diff --git a/apps/plugins/superdom.c b/apps/plugins/superdom.c index 91747edb80..3cbc5f285d 100644 --- a/apps/plugins/superdom.c +++ b/apps/plugins/superdom.c @@ -33,9 +33,55 @@ char buf[255]; +/* key mappings */ + +#define SUPERDOM_OK PLA_SELECT +#define SUPERDOM_CANCEL PLA_CANCEL +#define SUPERDOM_RIGHT PLA_RIGHT +#define SUPERDOM_LEFT PLA_LEFT +#define SUPERDOM_UP PLA_UP +#define SUPERDOM_DOWN PLA_DOWN + +#define SUPERDOM_RIGHT_REPEAT PLA_RIGHT_REPEAT +#define SUPERDOM_LEFT_REPEAT PLA_LEFT_REPEAT +#define SUPERDOM_UP_REPEAT PLA_UP_REPEAT +#define SUPERDOM_DOWN_REPEAT PLA_DOWN_REPEAT + +/***** game settings *****/ + +/* Some defines for the prices */ +#define PRICE_MEN 1 +#define PRICE_MOVE 100 +#define PRICE_TANK 300 +#define PRICE_PLANE 600 +#define PRICE_FARM 1150 +#define PRICE_FACTORY 1300 +#define PRICE_NUKE 2000 + +#define STRINGIZE_2(X) #X +#define STRINGIZE(X) STRINGIZE_2(X) + +#define PRICE_MEN_STR STRINGIZE(PRICE_MEN) +#define PRICE_MOVE_STR STRINGIZE(PRICE_MOVE) +#define PRICE_TANK_STR STRINGIZE(PRICE_TANK) +#define PRICE_PLANE_STR STRINGIZE(PRICE_PLANE) +#define PRICE_FARM_STR STRINGIZE(PRICE_FARM) +#define PRICE_FACTORY_STR STRINGIZE(PRICE_FACTORY) +#define PRICE_NUKE_STR STRINGIZE(PRICE_NUKE) + +/* surrender thresholds */ +#define HUMAN_SURRENDER_THRESHOLD 15 +#define COMPUTER_SURRENDER_THRESHOLD 15 +#define COMPUTER_HARD_SURRENDER_THRESHOLD 25 + +/* board size */ +#define BOARD_SIZE 10 +#define NUM_SPACES (BOARD_SIZE*BOARD_SIZE) #define COLOUR_DARK 0 #define COLOUR_LIGHT 1 +/* drawing presets */ + #define MARGIN 5 #if (LCD_DEPTH >= 16) @@ -45,11 +91,11 @@ char buf[255]; #endif #if LCD_WIDTH > LCD_HEIGHT -#define BOX_WIDTH ((LCD_WIDTH-(MARGIN*2))/10) +#define BOX_WIDTH ((LCD_WIDTH-(MARGIN*2))/BOARD_SIZE) #define BOX_HEIGHT ((BOX_WIDTH*2)/3) #else -#define BOX_HEIGHT ((LCD_HEIGHT-(MARGIN*2)-15)/10) +#define BOX_HEIGHT ((LCD_HEIGHT-(MARGIN*2)-15)/BOARD_SIZE) #define BOX_WIDTH ((BOX_HEIGHT*2)/3) #endif @@ -72,38 +118,6 @@ char buf[255]; #define ICON_HEIGHT (BMPHEIGHT_superdom_boarditems/6) #define ICON_WIDTH (BMPWIDTH_superdom_boarditems/2) -#define SUPERDOM_OK PLA_SELECT -#define SUPERDOM_CANCEL PLA_CANCEL -#define SUPERDOM_RIGHT PLA_RIGHT -#define SUPERDOM_LEFT PLA_LEFT -#define SUPERDOM_UP PLA_UP -#define SUPERDOM_DOWN PLA_DOWN - -#define SUPERDOM_RIGHT_REPEAT PLA_RIGHT_REPEAT -#define SUPERDOM_LEFT_REPEAT PLA_LEFT_REPEAT -#define SUPERDOM_UP_REPEAT PLA_UP_REPEAT -#define SUPERDOM_DOWN_REPEAT PLA_DOWN_REPEAT - -/* Some defines for the prices */ -#define PRICE_MEN 1 -#define PRICE_MOVE 100 -#define PRICE_TANK 300 -#define PRICE_PLANE 600 -#define PRICE_FARM 1150 -#define PRICE_FACTORY 1300 -#define PRICE_NUKE 2000 - -#define STRINGIZE_2(X) #X -#define STRINGIZE(X) STRINGIZE_2(X) - -#define PRICE_MEN_STR STRINGIZE(PRICE_MEN) -#define PRICE_MOVE_STR STRINGIZE(PRICE_MOVE) -#define PRICE_TANK_STR STRINGIZE(PRICE_TANK) -#define PRICE_PLANE_STR STRINGIZE(PRICE_PLANE) -#define PRICE_FARM_STR STRINGIZE(PRICE_FARM) -#define PRICE_FACTORY_STR STRINGIZE(PRICE_FACTORY) -#define PRICE_NUKE_STR STRINGIZE(PRICE_NUKE) - enum { RET_VAL_OK, RET_VAL_USB, @@ -149,6 +163,21 @@ static struct settings { int startcash; int startfood; int movesperturn; + /* 1=easy 2=medium 3=hard */ + /* AI difficulty works like this: + easy: + - no movement + - no investing + medium: + - movement + - investing + - can build factories/farms + hard: + - nuclear war + - harder to surrender + */ + int compdiff; + bool spoil_enabled; } superdom_settings; static struct resources humanres; @@ -161,7 +190,7 @@ static struct cursor { int y; } cursor; -static struct tile board[12][12]; +static struct tile board[BOARD_SIZE+2][BOARD_SIZE+2]; static const struct button_mapping *plugin_contexts[] = { pla_main_ctx }; @@ -171,9 +200,9 @@ static void init_board(void) rb->srand(*rb->current_tick); for(i=0;i<12;i++) { /* Hopefully about 50% each colour */ - for(j=0;j<12;j++) + for(j=0;j10)||(j>10)) + if((i<1)||(j<1)||(i>BOARD_SIZE)||(j>BOARD_SIZE)) board[i][j].colour = -1; /* Unset */ else board[i][j].colour = rb->rand()%2; @@ -188,8 +217,8 @@ static void init_board(void) while(compres.farms < superdom_settings.compstartfarms) { - i = rb->rand()%10 + 1; - j = rb->rand()%10 + 1; + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; if((board[i][j].colour == COLOUR_DARK) && (board[i][j].farm == false)) { board[i][j].farm = true; @@ -198,8 +227,8 @@ static void init_board(void) } while(compres.inds < superdom_settings.compstartinds) { - i = rb->rand()%10 + 1; - j = rb->rand()%10 + 1; + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; if((board[i][j].colour == COLOUR_DARK) && (board[i][j].ind == false)) { board[i][j].ind = true; @@ -208,8 +237,8 @@ static void init_board(void) } while(humanres.farms < superdom_settings.humanstartfarms) { - i = rb->rand()%10 + 1; - j = rb->rand()%10 + 1; + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; if((board[i][j].colour == COLOUR_LIGHT)&&(board[i][j].farm == false)) { board[i][j].farm = true; @@ -218,8 +247,8 @@ static void init_board(void) } while(humanres.inds < superdom_settings.humanstartinds) { - i = rb->rand()%10 + 1; - j = rb->rand()%10 + 1; + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; if((board[i][j].colour == COLOUR_LIGHT) && (board[i][j].ind == false)) { board[i][j].ind = true; @@ -232,9 +261,9 @@ void draw_board(void) { int i,j; rb->lcd_clear_display(); - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(board[i][j].colour == COLOUR_DARK) { @@ -325,14 +354,14 @@ void draw_board(void) } rb->lcd_set_foreground(LCD_BLACK); /* Draw Horizontal lines */ - for(i=0;i<=10;i++) + for(i=0;i<=BOARD_SIZE;i++) { - rb->lcd_hline(MARGIN, MARGIN+(BOX_WIDTH*10), MARGIN+(BOX_HEIGHT*i)); + rb->lcd_hline(MARGIN, MARGIN+(BOX_WIDTH*BOARD_SIZE), MARGIN+(BOX_HEIGHT*i)); } /* Draw Vertical lines */ - for(i=0;i<=10;i++) + for(i=0;i<=BOARD_SIZE;i++) { - rb->lcd_vline(MARGIN+(BOX_WIDTH*i), MARGIN, MARGIN+(BOX_HEIGHT*10)); + rb->lcd_vline(MARGIN+(BOX_WIDTH*i), MARGIN, MARGIN+(BOX_HEIGHT*BOARD_SIZE)); } rb->lcd_update(); } @@ -346,15 +375,30 @@ static int calc_strength(int colour, int x, int y) { if(board[x+a][y+b].colour==colour) { - score += 10; - if(board[x + a][y + b].tank || board[x + a][y + b].farm) - score += 30; - if(board[x + a][y + b].plane || board[x + a][y + b].ind) - score += 40; - if(board[x + a][y + b].nuke) - score += 20; - if(board[x + a][y + b].men) - score += (board[x + a][y + b].men*133/1000); + if(a && b) /* diagonally adjacent, give less influence */ + { + score += 5; + if(board[x + a][y + b].tank || board[x + a][y + b].farm) + score += 15; + if(board[x + a][y + b].plane || board[x + a][y + b].ind) + score += 20; + if(board[x + a][y + b].nuke) + score += 10; + if(board[x + a][y + b].men) + score += (board[x + a][y + b].men*133/1000); + } + else + { + score += 10; + if(board[x + a][y + b].tank || board[x + a][y + b].farm) + score += 30; + if(board[x + a][y + b].plane || board[x + a][y + b].ind) + score += 40; + if(board[x + a][y + b].nuke) + score += 20; + if(board[x + a][y + b].men) + score += (board[x + a][y + b].men*133/1000); + } } } } @@ -468,6 +512,7 @@ void gen_resources(void) static void update_score(void) { int strength; + rb->lcd_setfont(FONT_SYSFIXED); rb->lcd_set_drawmode(DRMODE_BG|DRMODE_INVERSEVID); rb->lcd_fillrect(5,LCD_HEIGHT-20,105,20); @@ -486,7 +531,7 @@ static int settings_menu(void) MENUITEM_STRINGLIST(menu, "Super Domination Settings", NULL, "Computer starting farms", "Computer starting factories", "Human starting farms", "Human starting factories", - "Starting cash", "Starting food", "Moves per turn"); + "Starting cash", "Starting food", "Computer difficulty","Food spoilage", "Moves per turn"); while(1) { @@ -523,6 +568,22 @@ static int settings_menu(void) 250, 0, 5000, NULL); break; case 6: + { + static const struct opt_items difficulty_options[3]={ + {"Easy", 1}, + {"Intermediate", 2}, + {"Hard", 3} + }; + rb->set_option("Computer difficulty", &superdom_settings.compdiff, + INT, difficulty_options, 3, NULL); + superdom_settings.compdiff++; + break; + } + case 7: + rb->set_bool_options("Food spoilage", &superdom_settings.spoil_enabled, "Enabled", + 0, "Disabled", 0, NULL); + break; + case 8: rb->set_int("Moves per turn", "", UNIT_INT, &superdom_settings.movesperturn, NULL, 1, 1, 5, NULL); @@ -1499,7 +1560,7 @@ static int select_square(void) } else { - cursor.x = 10; + cursor.x = BOARD_SIZE; } update_score(); draw_cursor(); @@ -1507,7 +1568,7 @@ static int select_square(void) case SUPERDOM_RIGHT: case SUPERDOM_RIGHT_REPEAT: draw_cursor(); /* Deselect the current tile */ - if(cursor.x<10) + if(cursor.x 1) cursor.x--; else - cursor.x = 10; + cursor.x = BOARD_SIZE; #endif - cursor.y = 10; + cursor.y = BOARD_SIZE; } update_score(); draw_cursor(); @@ -1541,14 +1602,14 @@ static int select_square(void) case SUPERDOM_DOWN: case SUPERDOM_DOWN_REPEAT: draw_cursor(); /* Deselect the current tile */ - if(cursor.y<10) + if(cursor.yinds -= board[x][y].ind; offres->farms += board[x][y].farm; offres->inds += board[x][y].ind; + offres->nukes += board[x][y].nuke; board[x][y].colour = colour; board[x][y].men = 0; board[x][y].tank = false; board[x][y].plane = false; - board[x][y].nuke = false; draw_board(); if(human) rb->sleep(HZ*2); @@ -1668,6 +1729,19 @@ static int attack_territory(int colour, int x, int y) return 0; } +static void spoil_food(void) +{ + /* spoil 0-10% of food, different amounts for computer/player */ + int spoil_amount=humanres.food*(0.1*rb->rand()/RAND_MAX); + if(spoil_amount) + rb->splashf(2*HZ, "Spoilage claims %d units of food", spoil_amount); + humanres.food-=spoil_amount; + + /* now for computer */ + spoil_amount=compres.food*(0.1*rb->rand()/RAND_MAX); + compres.food-=spoil_amount; +} + static int war_menu(void) { int selection = 0, temp; @@ -1782,7 +1856,7 @@ static void computer_allocate(void) { /* Firstly, decide whether to go offensive or defensive. * This is primarily decided by the human player posing a threat to either - * the computer's farms or factories */ + * the computer's farms, factories or nukes */ int i, j, k; bool offensive = true; struct threat threats[4]; @@ -1797,17 +1871,18 @@ static void computer_allocate(void) compres.cash += compres.bank; compres.bank = 0; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(board[i][j].colour == COLOUR_DARK) { numterritory++; str_diff = calc_strength(COLOUR_LIGHT,i,j) - calc_strength(COLOUR_DARK,i,j); - if(str_diff > 0 && (board[i][j].ind || board[i][j].farm)) + if(str_diff > 0 && (board[i][j].ind || board[i][j].farm || board[i][j].nuke)) { + /* computer's farm/factory/nuke is being threatened */ if(numthreats < 3) { offensive = false; @@ -1821,10 +1896,24 @@ static void computer_allocate(void) rb->yield(); } } + /* AI player will buy nukes if possible first */ + if(compres.cash > PRICE_NUKE + PRICE_TANK && superdom_settings.compdiff>=3) + { + while(compres.cash >= PRICE_NUKE && compres.nukes < numterritory) + { + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; + if(board[i][j].colour == COLOUR_DARK) + { + buy_resources(COLOUR_DARK, 5, i, j, 0); + } + rb->yield(); + } + } if(offensive) { /* The AI is going to go straight for the throat here and attack - * the player's farms and factories. The amount of cash + * the player's farms, nukes, and factories. The amount of cash * the AI has to spend will determine how many targets there are */ if(compres.cash > 1200) { @@ -1839,12 +1928,12 @@ static void computer_allocate(void) * owned by the computer. If none are found just place troops in * random places around the map until we run out of money */ k = 0; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(has_adjacent(i,j) && - (board[i][j].ind || board[i][j].farm)) + (board[i][j].ind || board[i][j].farm || board[i][j].nuke)) { if(k= 300 && compres.tanks < numterritory) { - i = rb->rand()%10 + 1; - j = rb->rand()%10 + 1; - if(board[i][j].colour == COLOUR_DARK) { + while(compres.cash >= 300 && compres.tanks < numterritory) + { + i = rb->rand()%BOARD_SIZE + 1; + j = rb->rand()%BOARD_SIZE + 1; + if(board[i][j].colour == COLOUR_DARK) + { buy_resources(COLOUR_DARK, 1, i, j, 0); } rb->yield(); @@ -1962,8 +2053,12 @@ static void computer_allocate(void) } } } - compres.bank += compres.cash; - compres.cash = 0; + /* no investing in easy mode */ + if(superdom_settings.compdiff>=2) + { + compres.bank += compres.cash; + compres.cash = 0; + } } static int find_adj_target(int x, int y, struct cursor* adj) @@ -2001,9 +2096,14 @@ static int find_adj_target(int x, int y, struct cursor* adj) return 0; } +static void computer_movement(void) +{ + +} + static void computer_war(void) { - /* Work out where to attack - prioritise the defence of buildings */ + /* Work out where to attack - prioritise the defence of buildings and nukes */ int i, j; bool found_target = true; struct cursor adj; @@ -2011,12 +2111,12 @@ static void computer_war(void) while(found_target) { found_target = false; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if((board[i][j].colour == COLOUR_DARK) && - (board[i][j].farm || board[i][j].ind) && + (board[i][j].farm || board[i][j].ind || board[i][j].nuke) && find_adj_target(i, j, &adj)) { found_target = true; @@ -2036,12 +2136,12 @@ static void computer_war(void) while(found_target) { found_target = false; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(board[i][j].colour == COLOUR_LIGHT && - (board[i][j].ind || board[i][j].farm) && + (board[i][j].ind || board[i][j].farm || board[i][j].nuke) && (calc_strength(COLOUR_DARK, i, j) >= calc_strength(COLOUR_LIGHT, i, j))) { found_target = true; @@ -2061,9 +2161,9 @@ static void computer_war(void) while(found_target) { found_target = false; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(board[i][j].colour == COLOUR_LIGHT && (calc_strength(COLOUR_DARK, i, j) >= @@ -2141,6 +2241,8 @@ static void default_settings(void) superdom_settings.startcash = 0; superdom_settings.startfood = 0; superdom_settings.movesperturn = 2; + superdom_settings.compdiff=2; + superdom_settings.spoil_enabled=false; } static int average_strength(int colour) @@ -2149,9 +2251,9 @@ static int average_strength(int colour) * used to determine when the computer wins or loses. */ int i,j; int totalpower = 0; - for(i=1;i<11;i++) + for(i=1;i<=BOARD_SIZE;i++) { - for(j=1;j<11;j++) + for(j=1;j<=BOARD_SIZE;j++) { if(board[i][j].colour != -1) { @@ -2159,7 +2261,7 @@ static int average_strength(int colour) } } } - return totalpower/100; + return totalpower/NUM_SPACES; } enum plugin_status plugin_start(const void* parameter) @@ -2220,12 +2322,15 @@ startyear: { int avg_str_diff = (average_strength(COLOUR_LIGHT) - average_strength(COLOUR_DARK)); - if(avg_str_diff > 15) + /* computer will hold out longer in hard mode */ + if(avg_str_diff > (superdom_settings.compdiff>=3 ? + COMPUTER_HARD_SURRENDER_THRESHOLD : + COMPUTER_SURRENDER_THRESHOLD)) { rb->splash(HZ*4, "The computer has surrendered. You win."); return PLUGIN_OK; } - if(-avg_str_diff > 15) + if(-avg_str_diff > HUMAN_SURRENDER_THRESHOLD) { rb->splash(HZ*4, "Your army have suffered terrible morale from" " the bleak prospects of winning. You lose."); @@ -2259,6 +2364,15 @@ startyear: return PLUGIN_OK; break; } + /* computer movement */ + computer_movement(); + + /* spoil food */ + if(superdom_settings.spoil_enabled) + { + spoil_food(); + } + /* feed men */ if(humanres.men) { -- cgit v1.2.3