:========================================= :========================================= :========================================= : SUDOKU for ColecoVision : by Luc Miron : : version 0.01 : : Last update: March 15th 2008 :========================================= :========================================= :========================================= :================= : Constants :================= DATA sudoku_constants CONST TRUE = 0 CONST FALSE = 1 CONST VRAM_PAT_ADR_1 = 0x0000 CONST VRAM_PAT_ADR_2 = 0x0800 CONST VRAM_PAT_ADR_3 = 0x1000 CONST VRAM_COL_ADR_1 = 0x2000 CONST VRAM_COL_ADR_2 = 0x2800 CONST VRAM_COL_ADR_3 = 0x3000 CONST VRAM_SPR_ATTR_ADR = 0x1B00 CONST LENGTH_SPR_ATTR_TABLE 8 : (4 bytes per sprite) x (2 sprites) CONST MODE_GAME_SELECT_SCREEN = 0 CONST MODE_MANAGE_GAME_SELECT_SCREEN = 1 CONST MODE_INIT_TITLE_SCREEN = 2 CONST MODE_MANAGE_TITLE_SCREEN = 3 CONST MODE_INIT_OPTION_SCREEN = 4 CONST MODE_MANAGE_OPTION_SCREEN = 5 CONST MODE_INIT_WAIT_SCREEN = 6 CONST MODE_MANAGE_WAIT_SCREEN = 7 CONST MODE_INIT_GAME_SCREEN = 8 CONST MODE_MANAGE_GAME_SCREEN = 9 CONST MODE_MANAGE_GAME_OVER_SCREEN = 10 CONST SKILL_MODE_NORMAL = 3 CONST SKILL_MODE_BEGINNER = 4 CONST TIMER_MODE_NORMAL = 0 CONST TIMER_MODE_COUNTDOWN = 1 CONST TIMER_MODE_OFF = 2 DEFINE sdk_grid_ref_x AS UBYTE ARRAY SIZE 9 = 44,60,76,100,116,132,156,172,188 DEFINE sdk_grid_ref_y AS UBYTE ARRAY SIZE 9 = 23,39,55,79,95,111,135,151,167 DEFINE sdk_grid_tile_x AS UBYTE ARRAY SIZE 9 = 6,8,10,13,15,17,20,22,24 DEFINE sdk_grid_tile_y AS UBYTE ARRAY SIZE 9 = 3,5,7,10,12,14,17,19,21 DEFINE blue_glow1 AS UBYTE ARRAY SIZE 8 = 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70 DEFINE blue_glow2 AS UBYTE ARRAY SIZE 8 = 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x40, 0x40 DEFINE blue_glow3 AS UBYTE ARRAY SIZE 8 = 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50 DEFINE blue_glow4 AS UBYTE ARRAY SIZE 8 = 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x40, 0x40 DEFINE blue_glow5 AS UBYTE ARRAY SIZE 8 = 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 DEFINE blue_glow6 AS UBYTE ARRAY SIZE 8 = 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 DEFINE red_glow1 AS UBYTE ARRAY SIZE 8 = 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 DEFINE red_glow2 AS UBYTE ARRAY SIZE 8 = 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x40, 0x40 DEFINE red_glow3 AS UBYTE ARRAY SIZE 8 = 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80 DEFINE red_glow4 AS UBYTE ARRAY SIZE 8 = 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x40, 0x40 DEFINE red_glow5 AS UBYTE ARRAY SIZE 8 = 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60 DEFINE red_glow6 AS UBYTE ARRAY SIZE 8 = 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x40, 0x40 DEFINE sdk_color_scroll_1 AS UBYTE ARRAY SIZE 8 = 0x60, 0x60, 0x80, 0x80, 0x90, 0x90, 0x80, 0x80 DEFINE sdk_color_scroll_2 AS UBYTE ARRAY SIZE 8 = 0x80, 0x80, 0x60, 0x60, 0x80, 0x80, 0x90, 0x90 DEFINE sdk_color_scroll_3 AS UBYTE ARRAY SIZE 8 = 0x90, 0x90, 0x80, 0x80, 0x60, 0x60, 0x80, 0x80 DEFINE sdk_color_scroll_4 AS UBYTE ARRAY SIZE 8 = 0x80, 0x80, 0x90, 0x90, 0x80, 0x80, 0x60, 0x60 DEFINE sdk_blue_digit_tiles AS UBYTE ARRAY = 191,192,193,194,195,196,197,198,199,200 DEFINE sdk_white_digit_tiles AS UBYTE ARRAY = 181,182,183,184,185,186,187,188,189,190 DEFINE sdk_yellow_digit_tiles AS UBYTE ARRAY = 92,93,94,95,96,97,98,99,100,101 ENDDATA RAM DEFINE nmi_clock AS BYTE = 0 DEFINE nmi_special AS BYTE = 0 DEFINE prev_button_state AS BYTE = 0 DEFINE prev_joy_state AS BYTE = 0 DEFINE prev_keypad_1 AS BYTE = 0 DEFINE new_button_state AS BYTE DEFINE new_joy_state AS BYTE DEFINE cursor_x AS BYTE DEFINE cursor_y AS BYTE DEFINE grid_cursor_x AS BYTE DEFINE grid_cursor_y AS BYTE DEFINE sprite_attr AS BYTE ARRAY SIZE LENGTH_SPR_ATTR_TABLE DEFINE main_grid AS BYTE ARRAY SIZE 9,9 DEFINE skill_level AS BYTE = SKILL_MODE_NORMAL DEFINE visual_aid AS BYTE = TRUE DEFINE timer_mode AS BYTE = TIMER_MODE_NORMAL DEFINE timer_display AS BYTE = TRUE DEFINE digit_1 AS BYTE = 2 DEFINE digit_2 AS BYTE = 0 DEFINE countdown_start AS BYTE = 20 DEFINE digit_id AS BYTE DEFINE success AS BYTE DEFINE digit_counter AS BYTE DEFINE timer_min AS BYTE DEFINE timer_sec AS BYTE DEFINE glow_mode AS BYTE DEFINE chosen_row AS BYTE DEFINE chosen_col AS BYTE DEFINE delay AS BYTE = 0 DEFINE byteslot1 AS UBYTE : Multi-purpose reserved byte ENDRAM :================= :================= : showSprites : : Expected parameter on stack: : - Number of sprites to display (UBYTE) : Value placed on stack: : - NONE :================= :================= SUB showSprites DEFINE num_sprites AS UBYTE LINKTO byteslot1 POP num_sprites IF (num_sprites < 32) THEN LET sprite_attr[num_sprites*4] = 0xD0 : marks end of sprite list COPYVRAM sprite_attr VRAM_SPR_ATTR_ADR ((num_sprites*4)+1) ELSE COPYVRAM sprite_attr VRAM_SPR_ATTR_ADR 128 ENDIF ENDSUB :================= :================= : updateClock : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :================= :================= SUB updateClock :------------------------ : If one second has elapsed, update timer variables. :------------------------ IF (nmi_clock = 60) THEN LET nmi_clock = 0 IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN IF (timer_sec > 0) THEN :------------------------ : Decrement seconds by 1. :------------------------ LET timer_sec = timer_sec - 1 ELSE IF (timer_min > 0) THEN :------------------------ : Decrement minutes by 1, and reset seconds to 59. :------------------------ LET timer_min = timer_min - 1 LET timer_sec = 59 ENDIF ELSE IF (timer_min < 100) THEN :------------------------ : Increment timer by 1 second. :------------------------ LET timer_sec = timer_sec + 1 :------------------------ : Do we need to increment the minutes? :------------------------ IF (timer_sec = 60) THEN LET timer_min = timer_min + 1 LET timer_sec = 0 ENDIF ENDIF ENDIF ENDIF ENDSUB :====================== :====================== : checkForGridCollision : : Expected parameters on stack: : - Row number of target space in grid (BYTE) : - Column number of target space in grid (BYTE) : Value placed on stack: : - 0 (true) or 1 (false) (UBYTE) :====================== :====================== SUB checkForGridCollision RETURNS UBYTE DEFINE col AS BYTE DEFINE row AS BYTE DEFINE number AS UBYTE DEFINE idx AS UBYTE DEFINE idx2 AS UBYTE DEFINE region_row AS UBYTE DEFINE region_col AS UBYTE POP col POP row LET number = ABS main_grid[row][col] :------------------------ : Check row and column for same number :------------------------ LOOP idx FOR 9 IF (ABS main_grid[row][idx] = number AND idx != col) OR \ (ABS main_grid[idx][col] = number AND idx != row) THEN PUSH TRUE RETURN ENDIF ENDLOOP :------------------------ : Identify region :------------------------ IF (row < 3) THEN LET region_row = 0 ELSE IF (row < 6) THEN LET region_row = 3 ELSE LET region_row = 6 ENDIF IF (col < 3) THEN LET region_col = 0 ELSE IF (col < 6) THEN LET region_col = 3 ELSE LET region_col = 6 ENDIF :------------------------ : Check region :------------------------ LOOP idx FOR 3 LOOP idx2 FOR 3 IF (ABS main_grid[region_row+idx][region_col+idx2] = number) THEN IF ((region_row+idx) != row OR (region_col+idx2) != col) THEN PUSH TRUE RETURN ENDIF ENDIF ENDLOOP ENDLOOP PUSH FALSE ENDSUB :====================== :====================== : gridSolved : : Expected parameters on stack: : - NONE : Value placed on stack: : - 0 (true) or 1 (false) (UBYTE) :====================== :====================== SUB gridSolved RETURNS UBYTE DEFINE row_check AS UBYTE DEFINE col_check AS UBYTE DEFINE result AS UBYTE :------------- : Check each grid position for a collision :------------- LOOP row_check FOR 9 LOOP col_check FOR 9 LET result = POPSUB checkForGridCollision AS UBYTE WITH row_check col_check IF result = TRUE THEN :--------------- : Collision found, so grid is not : solved yet. Stop and return false. :--------------- PUSH FALSE RETURN ENDIF ENDLOOP ENDLOOP :------------- : No collision found, so return true. :------------- PUSH TRUE ENDSUB :====================== :====================== : getRow : : Expected parameter on stack: : - Vertical position inside the grid (0 to 8) (UBYTE) : Value placed on stack: : - Region number (0, 1 or 2) (UBYTE) :====================== :====================== SUB getRow RETURNS UBYTE DEFINE pos AS UBYTE LINKTO byteslot1 POP pos IF (pos < 3) THEN PUSH 0 ELSE IF (pos < 6) THEN PUSH 1 ELSE PUSH 2 ENDIF ENDSUB :====================== :====================== : getCol : : Expected parameter on stack: : - Horizontal position inside the grid (0 to 8) (UBYTE) : Value placed on stack: : - Region number (0, 1 or 2) (UINT) :====================== :====================== SUB getCol RETURNS UBYTE DEFINE pos AS UBYTE LINKTO byteslot1 POP pos IF (pos = 0 OR pos = 3 OR pos = 6) THEN PUSH 0 ELSE IF (pos = 1 OR pos = 4 OR pos = 7) THEN PUSH 1 ELSE PUSH 2 ENDIF ENDSUB :====================== :====================== : pickPosition : : Expected parameter on stack: : - Horizontal position of target region in grid (0,3 or 6) (UBYTE) : - Vertical position of target region in grid (0,3 or 6) (UBYTE) : - Number to insert in grid (BYTE) : Value placed on stack: NONE :====================== :====================== SUB pickPosition DEFINE cur_num AS BYTE DEFINE region_row AS UBYTE DEFINE region_col AS UBYTE DEFINE idx AS UBYTE DEFINE range AS UBYTE DEFINE cur_rom AS UBYTE DEFINE cur_col AS UBYTE DEFINE ok AS UBYTE DEFINE pos AS UBYTE DEFINE swap AS UBYTE DEFINE choice AS UINT DEFINE chosen_pos AS UBYTE DEFINE temp AS UBYTE DEFINE test AS UBYTE ARRAY SIZE 9 POP region_col POP region_row POP cur_num :------------------------ : Initialize the test array :------------------------ LOOP idx FOR 9 LET test[idx] = idx LET range = 9 :------------------------ : Put aside inappropriate grid positions :------------------------ LOOP idx FOR 9 LET cur_row = region_row + (POPSUB getRow AS UBYTE WITH idx) LET cur_col = region_col + (POPSUB getCol AS UBYTE WITH idx) LET ok = TRUE IF (main_grid[cur_row][cur_col] != 0) THEN :------------------------ : Already occupied by other number :------------------------ LET ok = FALSE ELSE :------------------------ : Check to see if same number is already present : in other region, on same row or column. :------------------------ LOOP pos FOR 9 STOPWHEN ok = FALSE IF (main_grid[cur_row][pos] = cur_num OR \ main_grid[pos][cur_col] = cur_num) THEN LET ok = FALSE ENDLOOP ENDIF IF (ok = FALSE) THEN LET range = range - 1 LOOP pos FOR 9 IF (test[pos] = idx) THEN LET temp = pos ENDLOOP LET swap = test[range] LET test[range] = test[temp] LET test[temp] = swap ENDIF IF (range = 0) THEN LET chosen_row = -1 ELSE LET choice = (POPSUB getRandomUInt AS UINT) MOD range LET chosen_pos = test[choice] LET chosen_row = region_row + (POPSUB getRow AS UBYTE WITH chosen_pos) LET chosen_col = region_col + (POPSUB getCol AS UBYTE WITH chosen_pos) ENDIF ENDLOOP ENDSUB :====================== :====================== : generateGrid : : Expected parameter on stack: : - NONE : Value placed on stack: : - 0 (true) or 1 (false) (UBYTE) :====================== :====================== SUB generateGrid RETURNS UBYTE DEFINE fail AS UBYTE DEFINE row AS UBYTE DEFINE col AS UBYTE DEFINE num_retries AS UBYTE DEFINE cur_num AS UBYTE DEFINE region_row AS UBYTE DEFINE region_col AS UBYTE DEFINE ok AS UBYTE LET fail = FALSE :------------------------ : Erase the progress bar :------------------------ PRINT "\t180\t180\t180\t180\t180\t180\t180\t180\t180\t180" AT 17,11 :------------------------ : Initialize grid :------------------------ LOOP row FOR 9 LOOP col FOR 9 LET main_grid[row][col] = 0 ENDLOOP ENDLOOP :------------------------ : Place numbers in grid :------------------------ LOOP cur_num FROM 1 TO 9 PRINT "\t001" AT 17,(10+cur_num) : Update progress bar on the screen LET num_retries = 0 LET chosen_row = -1 WHILE (chosen_row < 0) :------------------------ : Do some cleaning :------------------------ LOOP row FOR 9 LOOP col FOR 9 IF (main_grid[row][col] = cur_num) THEN LET main_grid[row][col] = 0 ENDLOOP ENDLOOP LET ok = TRUE :----------------- : Insert the current digit (cur_num) in a : single spot within each of the nine regions. :----------------- LOOP row FOR 3 STOPWHEN ok = FALSE LET region_row = row * 3 LOOP col FOR 3 STOPWHEN ok = FALSE LET region_col = col * 3 GOSUB pickPosition WITH cur_num region_row region_col IF (chosen_row >= 0) THEN LET main_grid[chosen_row][chosen_col] = cur_num ELSE LET ok = FALSE LET num_retries = num_retries + 1 IF (num_retries = 10) THEN LET fail = TRUE LET chosen_row = 0 ENDIF ENDIF ENDLOOP ENDLOOP ENDWHILE ENDLOOP PUSH fail ENDSUB :====================== :====================== : hideNumbers : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :====================== :====================== SUB hideNumbers DEFINE idx AS UBYTE DEFINE idx2 AS UBYTE DEFINE chosen AS UINT :------------------------ : Turn up to four digits per line into solid digits :------------------------ LOOP idx FOR 9 LOOP idx2 FOR skill_level LET chosen = (POPSUB getRandomUInt AS UINT) MOD 9 IF (main_grid[idx][chosen] > 0) THEN :-------------- : A negative value indicates a solid digit :-------------- LET main_grid[idx][chosen] = -main_grid[idx][chosen] ENDIF ENDLOOP :------------------------ : Turn the rest of the digits into zeros (empty spaces in grid) :------------------------ LOOP idx2 FOR 9 IF (main_grid[idx][idx2] > 0) THEN LET main_grid[idx][idx2] = 0 ENDLOOP ENDLOOP ENDSUB :================= :================= : printOptionValues : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :================= :================= SUB printOptionValues :------------------ : Print skill level value in name table ("NORMAL" or "BEGINNER") :------------------ IF (skill_level = SKILL_MODE_NORMAL) THEN IF (cursor_y = 28) THEN : Print "NORMAL " in blue letters PRINT "\t167\t168\t171\t166\t154\t165\t180\t180\t180\t180" AT 4,21 ELSE : Print "NORMAL " in white letters PRINT "\t115\t116\t119\t114\t102\t113\t180\t180\t180\t180" AT 4,21 ENDIF ELSE IF (cursor_y = 28) THEN : Print "BEGINNER" in blue letters PRINT "\t155\t158\t160\t162\t167\t167\t158\t171" AT 4,21 ELSE : Print "BEGINNER" in white letters PRINT "\t103\t106\t108\t110\t115\t115\t106\t119" AT 4,21 ENDIF ENDIF :------------------ : Print visual aid value in name table ("ON" or "OFF") :------------------ IF (visual_aid = TRUE) THEN IF (cursor_y = 44) THEN : Print "ON " in blue letters PRINT "\t168\t167\t180" AT 6,21 ELSE : Print "ON " in white letters PRINT "\t116\t115\t180" AT 6,21 ENDIF ELSE IF (cursor_y = 44) THEN : Print "OFF " in blue letters PRINT "\t168\t159\t159\t180\t180\t180\t180\t180\t180" AT 6,21 ELSE : Print "OFF " in white letters PRINT "\t116\t107\t107\t180\t180\t180\t180\t180\t180" AT 6,21 ENDIF ENDIF :------------------ : Print timer mode value in name table ("NORMAL", "COUNTDOWN" or "OFF") :------------------ IF (timer_mode = TIMER_MODE_NORMAL) THEN IF (cursor_y = 60) THEN : Print "NORMAL " in blue letters PRINT "\t167\t168\t171\t166\t154\t165\t180\t180\t180\t180" AT 8,21 ELSE : Print "NORMAL " in white letters PRINT "\t115\t116\t119\t114\t102\t113\t180\t180\t180\t180" AT 8,21 ENDIF ELSE IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN IF (cursor_y = 60) THEN : Print "COUNTDOWN" in blue letters PRINT "\t156\t168\t174\t167\t173\t157\t168\t176\t167" AT 8,21 ELSE : Print "COUNTDOWN" in white letters PRINT "\t104\t116\t122\t115\t121\t105\t116\t124\t115" AT 8,21 ENDIF ELSE : (timer_mode = TIMER_MODE_OFF) IF (cursor_y = 60) THEN : Print "OFF " in blue letters PRINT "\t168\t159\t159\t180\t180\t180\t180\t180\t180" AT 8,21 ELSE : Print "OFF " in white letters PRINT "\t116\t107\t107\t180\t180\t180\t180\t180\t180" AT 8,21 ENDIF ENDIF :------------------ : If timer mode = "COUNTDOWN", print digit_1 and digit_2 in name table :------------------ IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN IF (cursor_y = 76) THEN : Print countdown start value with blue digits PRINT countdown_start USING sdk_blue_digit_tiles AT 10,21 ELSE : Print countdown start value with white digits PRINT countdown_start USING sdk_white_digit_tiles AT 10,21 ENDIF ELSE : Print dashes instead of digits PRINT "\t95\t95" AT 10,21 ENDIF :------------------ : Print music tune setting in name table :------------------ PRINT "\t95\t95" AT 12,21 : Disabled for now, so print dashes :------------------ : Print sound effects setting in name table :------------------ PRINT "\t95\t95" AT 14,21 : Disabled for now, so print dashes ENDSUB :================= :================= : updateVisualAid : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :================= :================= SUB updateVisualAid DEFINE row AS UBYTE DEFINE col AS UBYTE DEFINE digit AS UBYTE DEFINE result AS UBYTE LOOP row FOR 9 LOOP col FOR 9 LET digit = ABS main_grid[row][col] IF (main_grid[row][col] < 0) THEN : Solid digit LET result = POPSUB checkForGridCollision AS UBYTE WITH rol col IF (result = TRUE) THEN : Display glowing blue digit PRINT TILE (digit*2)+71) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+72) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ELSE : Display regular blue digit PRINT TILE (digit*2)+17) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+18) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ENDIF ELSE IF (main_grid[row][col] != 0) THEN : Non-solid digit LET result = POPSUB checkForGridCollision AS UBYTE WITH row col IF (result = TRUE) THEN : Display glowing red digit PRINT TILE (digit*2)+53) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+54) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ELSE : Display regular green digit PRINT TILE (digit*2)+35) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+36) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ENDIF ENDIF ENDLOOP ENDLOOP ENDSUB :================= :================= : removeVisualAid : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :================= :================= SUB removeVisualAid DEFINE row AS UBYTE DEFINE col AS UBYTE DEFINE digit AS UBYTE LOOP row FOR 9 LOOP col FOR 9 LET digit = ABS main_grid[row][col] IF (main_grid[row][col] < 0) THEN : Solid digit : Display regular blue digit PRINT TILE (digit*2)+17) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+18) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ELSE IF (main_grid[row][col] != 0) THEN : Non-solid digit : Display regular green digit PRINT TILE (digit*2)+35) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col] PRINT TILE (digit*2)+36) AT sdk_grid_tile_x[row],sdk_grid_tile_y[col]+1 ENDIF ENDLOOP ENDLOOP ENDSUB :====================== :====================== : updateGlowingDigits : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :====================== :====================== SUB updateGlowingDigits DEFINE blue_glow_top_addr AS UINT DEFINE blue_glow_bot_addr AS UINT DEFINE red_glow_top_addr AS UINT DEFINE red_glow_bot_addr AS UINT DEFINE tile_idx AS UBYTE IF (glow_mode = 0) THEN LET blue_glow_top_addr = blue_glow1 LET blue_glow_bot_addr = blue_glow2 LET red_glow_top_addr = red_glow1 LET red_glow_bot_addr = red_glow2 ELSE IF (glow_mode = 1 OR glow_mode = 3) THEN LET blue_glow_top_addr = blue_glow3 LET blue_glow_bot_addr = blue_glow4 LET red_glow_top_addr = red_glow3 LET red_glow_bot_addr = red_glow4 ELSE IF (glow_mode = 2) THEN LET blue_glow_top_addr = blue_glow5 LET blue_glow_bot_addr = blue_glow6 LET red_glow_top_addr = red_glow5 LET red_glow_bot_addr = red_glow6 ENDIF :----------------- : Update blue glowing digits on screen :----------------- LOOP tile_idx FROM 73 TO 89 GOSUB changeMultiColor WITH tile_idx blue_glow_top_addr GOSUB changeMultiColor WITH (tile_idx+1) blue_glow_bot_addr LET tile_idx = tile_idx + 1 : Incrementation by 2 is intentional here ENDLOOP :----------------- : Update red glowing digits on screen :----------------- LOOP tile_idx FROM 55 TO 71 GOSUB changeMultiColor WITH tile_idx red_glow_top_addr GOSUB changeMultiColor WITH (tile_idx+1) red_glow_bot_addr LET tile_idx = tile_idx + 1 : Incrementation by 2 is intentional here ENDLOOP LET glow_mode = glow_mode + 1 IF (glow_mode > 3) THEN LET glow_mode = 0 ENDSUB :====================== :====================== : updateGlowingBar : : Expected parameter on stack: : - NONE : Value placed on stack: : - NONE :====================== :====================== SUB updateGlowingBar IF (glow_mode = 0) THEN GOSUB changeMultiColor WITH 95 blue_glow1 ELSE IF (glow_mode = 1 OR glow_mode = 3) THEN GOSUB changeMultiColor WITH 95 blue_glow3 ELSE IF (glow_mode = 2) THEN GOSUB changeMultiColor WITH 95 blue_glow5 ENDIF LET glow_mode = glow_mode + 1 IF (glow_mode > 3) THEN LET glow_mode = 0 ENDSUB :================= :================= : NMI BLOCK :================= :================= NMI :------------------ : Increment system clock :------------------ LET nmi_clock = nmi_clock + 1 :------------------ : Register joystick and button states :------------------ UPDATE JOY1 LET new_button_state = JOY1_FIRE_LEFT + JOY1_FIRE_RIGHT LET new_joy_state = JOY1_DIRECTION :------------------ : If currently in the wait screen, update : the color of the glowing progress bar. :------------------ IF (nmi_special = MODE_MANAGE_WAIT_SCREEN) THEN IF ((nmi_clock MOD 30) = 0) THEN GOSUB updateGlowingBar ENDIF ENDNMI :================= :================= : SudokuTitleScreen (GAME LOOP) :================= :================= GAMELOOP SudokuTitleScreen INIT :------------------ : Disable screen and NMI while we're updating VRAM :------------------ GOSUB screenOff GOSUB disableNMI :------------------ : Load graphic set #1 into VRAM :------------------ GOSUB loadPatternRLE WITH sdk_pattern_set_1_rle GOSUB loadColorRLE WITH sdk_color_set_1_rle :------------------ : Load sprite patterns into VRAM :------------------ GOSUB loadSpritePatterRLE WITH sdk_sprite_pattern_set_1_rle :------------------ : Load title screen into name table :------------------ GOSUB loadNameRLE WITH sdk_title_screen_tileset_rle :------------------ : Position selection cursor on "START" :------------------ LET cursor_y = 100 LET sprite_attr[0] = cursor_y : sprite #1, y position LET sprite_attr[1] = 73 : sprite #1, x position LET sprite_attr[2] = 0 : sprite #1, pattern = arrow LET sprite_attr[3] = 3 : sprite #1, color = light green :------------------ : Okay, we're ready to start the actual loop :------------------ GOSUB screenOn GOSUB enableNMI ENDINIT :------------------ : Refresh selection cursor position on screen (in VRAM sprite array) :------------------ LET sprite_attr[0] = cursor_y GOSUB showSprites WITH 1 :------------------ : Process controller input :------------------ IF (prev_button_state != new_button_state) THEN LET rev_button_state = new_button_state IF (new_button_state != 0) THEN :------------------ : User has selected item in menu :------------------ IF (cursor_y = 100) THEN : cursor is on START JUMPTO SudokuWaitScreen ELSE IF (cursor_y = 124) THEN : cursor is on OPTIONS JUMPTO SudokuOptionScreen ENDIF ELSE IF (new_joy_state != prev_joy_state) THEN LET prev_joy_state = new_joy_state :------------------ : User moved the joystick (only UP and DOWN are processsed) :------------------ IF (JOY1_DIRECTION = NORTH OR JOY1_DIRECTION = SOUTH) THEN IF (cursor_y = 100) THEN : cursor is on START LET cursor_y = 124 : move cursor to OPTIONS ELSE LET cursor_y = 100 : move cursor to START ENDIF ENDIF ENDIF ENDGAMELOOP :================= :================= : SudokuOptionScreen (GAME LOOP) :================= :================= GAMELOOP SudokuOptionScreen DEFINE k AS UBYTE DEFINE change_occured AS UBYTE INIT :------------------ : Disable screen and NMI while we're updating VRAM :------------------ GOSUB screenOff GOSUB disableNMI :------------------ : This screen uses the same pattern/color table : as the title screen, so all we need to do is : load the option screen into the name table. :------------------ GOSUB loadNameRLE WITH sdk_option_screen_tileset_rle LET cursor_y = 28 LET sprite_attr[0] = cursor_y : sprite #1, y position LET sprite_attr[1] = 15 : sprite #1, x position LET sprite_attr[2] = 0 : sprite #1, pattern = arrow LET sprite_attr[3] = 5 : sprite #1, color = cyan :------------------ : Display current option values :------------------ GOSUB printOptionValues :------------------ : Set keypad focus on first digit of TIMER START value :------------------ LET digit_id = 0 :------------------ : Okay, we're ready to start the actual loop :------------------ GOSUB screenOn GOSUB enableNMI ENDINIT LET k = JOY1_KEYPAD LET change_occured = FALSE :------------------ : Refresh option cursor position on screen :------------------ LET sprite_attr[0] = cursor_y GOSUB showSprites WITH 1 :------------------ : Process controller input :------------------ IF (prev_button_state != new_button_state) THEN LET prev_button_state = new_button_state IF (new_button_state != 0) THEN :------------------ : User has pressed button to change setting in option menu :------------------ IF (cursor_y = 28) THEN : SKILL LEVEL IF (skill_level = SKILL_MODE_NORMAL) THEN LET skill_level = SKILL_MODE_BEGINNER ELSE LET skill_level = SKILL_MODE_NORMAL ENDIF ELSE IF (cursor_y = 44) THEN : VISUAL AID IF (visual_aid = TRUE) THEN LET visual_aid = FALSE ELSE LET visual_aid = TRUE ENDIF ELSE IF (cursor_y = 60) THEN : TIMER IF (timer_mode = TIMER_MODE_NORMAL) THEN LET timer_mode = TIMER_MODE_COUNTDOWN ELSE IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN LET timer_mode = TIMER_MODE_OFF ELSE LET timer_mode = TIMER_MODE_NORMAL ENDIF ENDIF :------------------ : Refresh option highlighting :------------------ LET change_occured = TRUE ENDIF ELSE IF (new_joy_state != prev_joy_state) THEN LET prev_joy_state = new_joy_state :------------------ : User moved the joystick (only UP and DOWN are processsed) :------------------ IF (JOY1_DIRECTION = DOWN) THEN IF (cursor_y = 28 LET cursor_y = 44 : cursor is on SKILL LEVEL so move to VISUAL AID ELSE IF (cursor_y = 44) THEN LET cursor_y = 60 : cursor is on VISUAL AID, so move to TIMER ELSE IF (cursor_y = 60) THEN : cursor is on TIMER... IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN LET cursor_y = 76 : ...so move cursor to TIMER START ELSE LET cursor_y = 28 : ...so move cursor to SKILL LEVEL ELSE IF (cursor_y = 76) THEN LET cursor_y = 28 : cursor is on TIMER START, so move cursor to SKILL LEVEL ENDIF ELSE IF (JOY1_DIRECTION = UP) THEN IF (cursor_y = 76) THEN LET cursor_y = 60 : cursor is on TIMER START, so move cursor to TIMER ELSE IF (cursor_y = 60) THEN LET cursor_y = 44 : cursor is on TIMER, so move cursor to VISUAL AID ELSE IF (cursor_y = 44) THEN LET cursor_y = 28 : cursor is on VISUAL AID, so move cursor to SKILL LEVEL ELSE IF (cursor_y = 28) THEN : cursor is on SKILL LEVEL... IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN LET cursor_y = 76 : ...so move cursor to TIMER START ELSE LET cursor_y = 60 : ... so move cursor to TIMER ENDIF ENDIF ENDIF :------------------ : Refresh option highlighting :------------------ LET change_occured = TRUE ELSE IF (k != prev_keypad_1) THEN LET prev_keypad_1 = k IF (k = 11) THEN :------------- : '#' key pressed, so return to title screen :------------- JUMPTO SudokuTitleScreen ELSE IF (cursor_y = 76) THEN : cursor is on TIMER START IF (k >= 0 && k < 10) THEN IF (digit_id = 0) THEN LET digit_1 = k LET digit_id = 1 : Go to second digit space ELSE LET digit_2 = k LET digit_id = 0 : Go back to first digit space ENDIF LET countdown_start = (digit_1 * 10) + digit_2 :------------------ : Refresh timer start setting on screen :------------------ LET change_occured = TRUE ENDIF ENDIF ENDIF :------------------ : Refresh options display on screen if change occured :------------------ IF (change_occured = TRUE) THEN GOSUB disableNMI GOSUB printOptionValues GOSUB enableNMI ENDIF ENDGAMELOOP :================= :================= : SudokuWaitScreen (GAME LOOP) :================= :================= GAMELOOP SudokuWaitScreen DEFINE x AS UBYTE DEFINE y AS UBYTE INIT :------------------ : Load "ONE MOMENT PLEASE" message into name table :------------------ GOSUB screenOff GOSUB disableNMI GOSUB loadNameRLE WITH sdk_wait_screen_tileset_rle GOSUB showSprites WITH 0 : Disable all sprites GOSUB screenOn GOSUB enableNMI ENDINIT :------------------ : Generate new Sudoku grid of numbers : (Retry until generateGrid() returns FALSE) :------------------ LET glow_mode = 0 LET nmi_special = MODE_MANAGE_WAIT_SCREEN #restart_gen_grid IF (POPSUB generateGrid) = TRUE) THEN GOTO #restart_gen_grid LET nmi_special = 0 :------------------ : Randomly select solid digits and turn them into negative : values, while setting all other digits to zero :------------------ GOSUB screenOff GOSUB hideNumbers :------------------ : Set digit counter to number of solid digits left in grid :------------------ LET digit_counter = 0 LOOP x FOR 9 LOOP y FOR 9 IF (main_grid[x][y] < 0) THEN LET digit_counter = digit_counter + 1 ENDIF ENDLOOP ENDLOOP :------------------ : Grid generated, now display it :------------------ JUMPTO SudokuGameScreen ENDGAMELOOP :================= :================= : SudokuGameScreen (GAME LOOP) :================= :================= GAMELOOP SudokuGameScreen DEFINE x AS UBYTE DEFINE y AS UBYTE DEFINE row AS UBYTE DEFINE col AS UBYTE DEFINE k AS UBYTE INIT :------------------ : Load graphic set #2 into VRAM :------------------ GOSUB disableNMI GOSUB loadPatternRLE WITH sdk_pattern_set_2_rle GOSUB loadColorRLE WITH sdk_color_set_2_rle :------------------ : Load grid display into name table :------------------ GOSUB loadNameRLE WITH sdk_game_screen_tileset_rle :------------------ : Place solid digits in grid on the screen :------------------ GOSUB updateVisualAid :------------------ : Set grid cursor at top-left corner of grid : (or closest if occupied by a solid digit) :------------------ LET cursor_x = 255 LOOP x FOR 9 STOPWHEN cursor_x != 255 LOOP y FOR 9 STOPWHEN cursor_x != 255 IF (main_grid[x][y] = 0) THEN LET cursor_x = sdk_grid_ref_x[x] LET cursor_y = sdk_grid_ref_y[y] LET grid_cursor_x = x LET grid_cursor_y = y ENDIF ENDLOOP ENDLOOP LET sprite_attr[0] = cursor_y : sprite #1, y position LET sprite_attr[1] = cursor_x : sprite #1, x position LET sprite_attr[2] = 4 : sprite #1, pattern = square grid cursor LET sprite_attr[3] = 10 : sprite #1, color = yellow :------------------ : Initialize timer :------------------ IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN IF (countdown_start = 0) THEN LET digit_1 = 2 LET digit_2 = 0 LET countdown_start = 20 ENDIF LET timer_min = countdown_start ELSE LET timer_min = 0 ENDIF LET timer_sec = 0 :------------------ : Reset system clock and glow mode variables :------------------ LET nmi_clock = 0 LET glow_mode = 0 :------------------ : Activate timer :------------------ IF (timer_mode != TIMER_MODE_OFF) THEN LET timer_display = TRUE ELSE LET timer_display = FALSE ENDIF :------------------ : Go to managing mode :------------------ GOSUB screenOn GOSUB enableNMI ENDINIT :------------------ : Update timer :------------------ GOSUB updateClock() LET k = JOY1_KEYPAD GOSUB disableNMI :------------------ : Update timer digits on screen :------------------ IF (timer_display = TRUE) THEN PRINT timer_min USING sdk_yellow_digit_tiles AT 1,13 PRINT "\t091" AT 1,15 PRINT timer_sec USING sdk_yellow_digit_tiles AT 1,16 ENDIF :------------------ : Erase the visual aid toggling message if delay has expired :------------------ IF (delay > 0) THEN LET delay = delay - 1 IF (delay = 0) THEN PRINT "\t131\t131\t131\t131\t131\t131\t131\t131\t131\t131\t131\t131\t131\t131" AT 0,9 ENDIF ENDIF :------------------ : Check for time-out :------------------ IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN IF (timer_min = 0 AND timer_sec = 0) THEN LET timer_display = FALSE :------------------ : Print "SORRY, OUT OF TIME!" failure message on screen :------------------ PRINT "\t123\t119\t122\t122\t129\t102\t131\t119\t125\t124\t131\t119\t110\t131\t124\t113\t117\t109\t104" AT 0,6 LET success = FALSE JUMPTO SudokuGameOverScreen ENDIF ENDIF :------------------ : Update glowing colors (in VRAM color table) every half second :------------------ IF ((nmi_clock MOD 30) = 0) THEN GOSUB updateGlowingDigits ENDIF :------------------ : Refresh grid cursor sprite position on screen :------------------ LET sprite_attr[0] = cursor_y LET sprite_attr[1] = cursor_x GOSUB showSprites WITH 1 :------------------ : Process controller input :------------------ IF (prev_button_state != new_button_state) THEN LET prev_button_state = new_button_state IF (new_button_state != 0) THEN :------------------- : Pressing any fire button erases existing digit :------------------- IF (main_grid[grid_cursor_x][grid_cursor_y] != 0) THEN LET main_grid[grid_cursor_x][grid_cursor_y] = 0 LET digit_counter = digit_counter - 1 :------------------ : Remove digit from grid on screen :------------------ PRINT "\t000" AT sdk_grid_tile_y[grid_cursor_y], sdk_grid_tile_x[grid_cursor_x] PRINT "\t002" AT sdk_grid_tile_y[grid_cursor_y]+1, sdk_grid_tile_x[grid_cursor_x] IF (visual_aid = TRUE) THEN GOSUB updateVisualAid ENDIF ENDIF ENDIF ELSE IF (new_joy_state != prev_joy_state) THEN LET prev_joy_state = new_joy_state IF (JOY1_DIRECTION = UP) THEN :---------------- : Moving cursor up. Wrap around to bottom if going off the grid. :---------------- IF (grid_cursor_y = 0) THEN LET grid_cursor_y = 8 ELSE LET grid_cursor_y = grid_cursor_y - 1 ENDIF :---------------- : Move cursor further up if cursor is currently on a solid digit. :---------------- WHILE (main_grid[grid_cursor_x][grid_cursor_y] < 0) IF (grid_cursor_y = 0) THEN LET grid_cursor_y = 8 ELSE LET grid_cursor_y = grid_cursor_y - 1 ENDIF ENDWHILE ELSE IF (JOY1_DIRECTION = DOWN) THEN :---------------- : Moving cursor down. Wrap around to top if going off the grid. :---------------- IF (grid_cursor_y = 8) THEN LET grid_cursor_y = 0 ELSE LET grid_cursor_y = grid_cursor_y + 1 ENDIF :---------------- : Move cursor further down if cursor is currently on a solid digit. :---------------- WHILE (main_grid[grid_cursor_x][grid_cursor_y] < 0) IF (grid_cursor_y = 8) THEN LET grid_cursor_y = 0 ELSE LET grid_cursor_y = grid_cursor_y + 1 ENDIF ENDWHILE ENDIF IF (JOY1_DIRECTION = LEFT) THEN :---------------- : Moving cursor left. Wrap around to right side if going off the grid. :---------------- IF (grid_cursor_x = 0) THEN LET grid_cursor_x = 8 ELSE LET grid_cursor_x = grid_cursor_x - 1 ENDIF :---------------- : Move cursor further left if cursor is currently on a solid digit. :---------------- WHILE (main_grid[grid_cursor_x][grid_cursor_y] < 0) THEN IF (grid_cursor_x = 0) THEN LET grid_cursor_x = 8 ELSE LET grid_cursor_x = grid_cursor_x - 1 ENDIF ENDWHILE ELSE IF (JOY1_DIRECTION = RIGHT) THEN :---------------- : Moving cursor right. Wrap around to left side if going off the grid. :---------------- IF (grid_cursor_x = 8) THEN LET grid_cursor_x = 0 ELSE LET grid_cursor_x = grid_cursor_x + 1 ENDIF :---------------- : Move cursor further right if cursor is currently on a solid digit. :---------------- WHILE (main_grid[grid_cursor_x][grid_cursor_y] < 0) IF (grid_cursor_x = 8) THEN LET grid_cursor_x = 0 ELSE LET grid_cursor_x = grid_cursor_x + 1 ENDIF ENDWHILE ENDIF LET cursor_x = sdk_grid_ref_x[grid_cursor_x] LET cursor_y = sdk_grid_ref_y[grid_cursor_y] ELSE IF (k != prev_keypad_1) THEN LET prev_keypad_1 = k IF (k = 0) THEN :------------------ : Key "0" on keypad toggles visual aid feature :------------------ IF (visual_aid = TRUE) THEN GOSUB removeVisualAid :----------------- : Print "VISUAL AID OFF" at top of screen :----------------- PRINT "\t126\t113\t123\t125\t105\t116\t131\t105\t113\t108\t131\t119\t110\t110" AT 0,9 LET visual_aid = FALSE ELSE GOSUB updateVisualAid :----------------- : Print "VISUAL AID ON " at top of screen :----------------- PRINT "\t126\t113\t123\t125\t105\t116\t131\t105\t113\t108\t131\t119\t118\t131" AT 0,9 LET visual_aid = TRUE ENDIF LET delay = 180 ELSE IF (k >= 1 AND k <= 9) THEN :----------------- : Digit pressed (1 to 9). Add 1 to digit counter if space is currently empty. :----------------- IF (main_grid[grid_cursor_x][grid_cursor_y] = 0) THEN LET digit_counter = digit_counter + 1 ENDIF :------------------ : Place digit in grid (in memory) :------------------ LET main_grid[grid_cursor_x][grid_cursor_y] = k :------------------ : Place digit in grid (on the screen) :------------------ IF (visual_aid = TRUE) THEN GOSUB updateVisualAid ELSE PRINT TILE (k*2)+35 AT sdk_grid_tile_y[grid_cursor_y], sdk_grid_tile_x[grid_cursor_x] PRINT TILE (k*2)+36 AT sdk_grid_tile_y[grid_cursor_y]+1, sdk_grid_tile_x[grid_cursor_x] ENDIF :------------------- : If all spaces in the grid are filled with digits, : then check for collisions. If no collisions are : detected, then the player has completed the puzzle! :------------------- IF (digit_counter = 81) THEN IF (POPSUB gridSolved) THEN LET timer_display = FALSE :------------------ : Reset all numbers to same color on screen, : so that the scrolling color effect will work : uniformally for all parts of the grid. :------------------ LOOP row FOR 9 LOOP col FOR 9 IF (main_grid[row][col] > 0) THEN LET main_grid[row][col] = -main_grid[row][col] ENDIF ENDLOOP ENDLOOP GOSUB updateVisualAid :------------------ : Print congratulation message at top of screen : ("NICE WORK!" and "TIME ELAPSED:") :------------------ PRINT "\t118\t113\t107\t109\t131\t127\t119\t122\t115\t104" AT 0,11 PRINT "\t109\t116\t105\t120\t123\t109\t108\t131\t124\t113\t117\t109\t131\t103" AT 1,5 IF (timer_mode = TIMER_MODE_COUNTDOWN) THEN LET timer_min = countdown_start - timer_min - 1 LET timer_sec = 60 - timer_sec ENDIF PRINT timer_min USING sdk_yellow_digit_tiles AT 1,21 PRINT "\t091" AT 1,23 PRINT timer_sec USING sdk_yellow_digit_tiles AT 1,24 :------------------ : Go to "game over" mode :------------------ LET success = TRUE JUMPTO SudokuGameOverScreen ENDIF ENDIF ENDIF ENDIF GOSUB enableNMI ENDGAMELOOP :================= :================= : SudokuGameOverScreen (GAME LOOP) :================= :================= GAMELOOP SudokuGameOverScreen DEFINE tile_num AS BYTE DEFINE colorset AS UINT INIT :---------------- : Disable all sprites :---------------- GOSUB showSprites WITH 0 ENDINIT IF (success = TRUE AND (nmi_clock MOD 15) = 0) THEN GOSUB disableNMI :------------------ : Update scrolling colors in color table :------------------ IF (glow_mode = 0) THEN LET colorset = sdk_color_scroll_1 ELSE IF (glow_mode = 1) THEN LET colorset = sdk_color_scroll_2 ELSE IF (glow_mode = 2) THEN LET colorset = sdk_color_scroll_3 ELSE IF (glow_mode = 3) THEN LET colorset = sdk_color_scroll_4 ENDIF :------------------ : Update colors in all relevant tiles on screen :------------------ LOOP tile_num FOR 37 GOSUB changeMultiColor WITH tile_num colorset ENDLOOP LET glow_mode = glow_mode + 1 IF (glow_mode > 3) THEN LET glow_mode = 0 ENDIF GOSUB enableNMI ENDIF IF (prev_button_state != new_button_state) THEN LET prev_button_state = new_button_state IF (new_button_state != 0) THEN :------------------ : Go back to title screen :------------------ JUMPTO SudokuTitleScreen ENDIF ENDIF ENDGAMELOOP :================= :================= : SudokuReset (GAME LOOP) :================= :================= GAMELOOP SudokuReset :------------------ : Clear VRAM :------------------ GOSUB screenOff GOSUB disableNMI WRITEVRAM 0 0x0000 16384 :------------------ : Initialize screen mode : (graphic mode #2, in "single pattern table" mode) :------------------ GOSUB InitScreenMode2 WITH 1 :------------- : Go to title screen :------------- JUMPTO SudokuTitleScreen ENDGAMELOOP START SudokuReset