Compare commits

..

No commits in common. 'main' and '0.6.0' have entirely different histories.
main ... 0.6.0

  1. 7
      dict/compress.py
  2. 5
      dict/solver.py
  3. 8
      inc/constants.asm
  4. 137
      inc/dict.asm
  5. 30
      inc/guess.asm
  6. 33
      inc/hints.asm
  7. 97
      inc/init.asm
  8. 47
      inc/input.asm
  9. 17
      inc/interrupts.asm
  10. 8
      inc/math.asm
  11. 38
      inc/messages.asm
  12. 22
      inc/oam.asm
  13. 113
      inc/search.asm
  14. 22
      inc/wincondition.asm
  15. 1
      maps/window-game.asm
  16. 8
      maps/window-help.asm
  17. 17
      tiles/sign-arrow.asm
  18. 17
      tiles/sign-slash.asm
  19. 121
      wordle.asm
  20. BIN
      wordle.gb

7
dict/compress.py

@ -1,11 +1,4 @@
#!/usr/bin/env python3
"""
To fit the whole game into one memory bank, the dictionary is compressed.
Since only 2^5 combinations are needed to store 26 letters,
5 bits would be enough; for simplicity's sake, however, we take 6.
This way we can reduce the memory consumption by 20%.
"""
import struct
def compress(in_path: str, out_path:str):

5
dict/solver.py

@ -1,9 +1,4 @@
#!/usr/bin/env python3
"""
Even though I like Wordle, it becomes very exhausting to constantly
use your brain while developing. That's why this tool exists.
"""
import random
print("Welcome to this debug tool!")

8
inc/constants.asm

@ -1,10 +1,4 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▄░█░▄▄▀█░▄▀▄░█░▄▄████░▄▄▀█▀▄▄▀█░██░████░▄▄▀█▀▄▄▀█░▄▄▀█░▄▄█▄░▄█░▄▄▀█░▄▄▀█▄░▄█░▄▄██
;; ██░█▀▀█░▀▀░█░█▄█░█░▄▄████░▄▄▀█░██░█░▀▀░████░████░██░█░██░█▄▄▀██░██░▀▀░█░██░██░██▄▄▀██
;; ██░▀▀▄█▄██▄█▄███▄█▄▄▄████░▀▀░██▄▄██▀▀▀▄████░▀▀▄██▄▄██▄██▄█▄▄▄██▄██▄██▄█▄██▄██▄██▄▄▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Hardware specific values and addresses
;; Game Boy Constants
; Interrupt Flags
INTERRUPT_SETTINGS EQU $ffff
INT_VBLANK_ON EQU %00000001

137
inc/dict.asm

@ -1,29 +1,15 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▄▀██▄██▀▄▀█▄░▄██▄██▀▄▄▀█░▄▄▀█░▄▄▀█░▄▄▀█░██░██
;; ██░██░██░▄█░█▀██░███░▄█░██░█░██░█░▀▀░█░▀▀▄█░▀▀░██
;; ██░▀▀░█▄▄▄██▄███▄██▄▄▄██▄▄██▄██▄█▄██▄█▄█▄▄█▀▀▀▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; This file contains all the functions to interact with the dictionary.
;; Therefore, a random word can be selected and it is possible to check if an entered word exists.
; Selects a random entry from the dictionary and saves it.
; Selects a random word from the dictionary.
; Sets the to be guessed and the revealed letter indices.
;
; Note:
; In the dictionary, entries with a word length of five are stored with only four bytes.
; Note: The dict saves the 5 chars of the words in 4 bytes.
; n+0: 11111122
; n+1: 22223333
; n+2: 33444444
; n+3: 55555500
; ~> 00111111 00222222 00333333 00444444 00555555
;
; -> [random_number]: depends on the current random number
; <- [currend_word]: five characters, index starts at one
select_word:
ld de, current_word
; determine the starting address of the dictionary entry
; (start address + random * word length)
ld a, [random_number+0]
and %00011111
ld h, a
@ -109,120 +95,3 @@ select_word:
ld [de], a
ret
; In order to check whether an entered word exists in the dictionary,
; it must first be compressed according to the dictionary.
; -> [current guess]: current rate attempt, stored in five bytes
; <- bc: first two bytes of the compression
; <- de: second two bytes of the compression
compress_guess:
call get_guess_offset
; first byte
ld a, [hl+] ; letter 1
and %00111111
sla a
sla a
ld b, a
ld a, [hl] ; letter 2
and %00110000
sra a
sra a
sra a
sra a
add a, b
ld d, a
; second byte
ld a, [hl+] ; letter 2
and %00001111
sla a
sla a
sla a
sla a
ld b, a
ld a, [hl] ; letter 3
and %00111100
sra a
sra a
add a, b
ld e, a
push de
; third byte
ld a, [hl+] ; letter 3
and %00000011
sla a
sla a
sla a
sla a
sla a
sla a
ld b, a
ld a, [hl+] ; letter 4
and %00111111
add a, b
ld d, a
; fourth byte
ld a, [hl] ; letter 5
and %00111111
sla a
sla a
ld e, a
pop bc
ret
; Try to find the current guess within the dictionary
; -> bc: first two bytes of the compression
; -> de: second two bytes of the compression
; <- a: whether the entry exists
find_guess:
ld hl, dictionary
.loop:
; check if the end of the dictionary is reached
push bc
ld bc, dictionary_end
ld a, h
cp a, b
jp nz, .not_eof
ld a, l
cp a, c
jp nz, .not_eof
pop bc
jp .return
.not_eof:
pop bc
ld a, [hl+]
cp a, b
jp nz, .add3
ld a, [hl+]
cp a, c
jp nz, .add2
ld a, [hl+]
cp a, d
jp nz, .add1
ld a, [hl+]
cp a, e
jp nz, .add0
ld a, 1
ret
.add3:
inc hl
.add2:
inc hl
.add1:
inc hl
.add0:
jp .loop
.return:
ld a, 0
ret

30
inc/guess.asm

@ -1,17 +1,7 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▄░█░██░█░▄▄█░▄▄█░▄▄█░███░█▀▄▄▀█░▄▄▀█░█▀██
;; ██░█▀▀█░██░█░▄▄█▄▄▀█▄▄▀█▄▀░▀▄█░██░█░▀▀▄█░▄▀██
;; ██░▀▀▄██▄▄▄█▄▄▄█▄▄▄█▄▄▄██▄█▄███▄▄██▄█▄▄█▄█▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Manages and displays the guessing attempts
; Updates the objects of the entered characters
; -> [guess_attempts]
; <- [obj_guess_letters]
update_guess_objects:
ld hl, obj_guess_letters
ld de, guess_attempts
ld de, guesses
ld a, $14
call update_guess_row
@ -30,10 +20,9 @@ update_guess_objects:
; Updates one line of entered characters
; -> hl: address of where to write obj data
; -> de: address of the current guess
; -> hl: position within obj data
; -> de: position within the guesses data
; -> a: vertical screen position
; <- [obj_guess_letters]
update_guess_row:
push hl
push hl
@ -42,14 +31,14 @@ update_guess_row:
; distance between objects
ld bc, 4
; write all five vertical positions
; vertical positions
ld [hl], a
REPT 4
add hl, bc
ld [hl], a
ENDR
; write all five horizontal positions
; horizontal positions
pop hl
inc hl
ld a, $30
@ -60,7 +49,7 @@ REPT 4
ld [hl], a
ENDR
; write all five tile indices
; tile indices
pop hl
inc hl
inc hl
@ -73,7 +62,7 @@ REPT 4
ld [hl], a
ENDR
; write all five palette info
; palette info
pop hl
inc hl
inc hl
@ -91,8 +80,7 @@ ENDR
; Calculates the address of the current guess attempt, using the number of the attempt
; -> [current_guess]: number of the current attempt
; Calculates the position of the current guess
; <- hl
get_guess_offset:
ld a, [current_guess]
@ -100,7 +88,7 @@ get_guess_offset:
call multiply_ab
ld b, 0
ld c, a
ld hl, guess_attempts
ld hl, guesses
add hl, bc
ret

33
inc/hints.asm

@ -1,19 +1,7 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░██░██▄██░▄▄▀█▄░▄█░▄▄██
;; ██░▄▄░██░▄█░██░██░██▄▄▀██
;; ██░██░█▄▄▄█▄██▄██▄██▄▄▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Manages the obtained hints
; Updates the background layer with the obtained hints.
; Note: a row within the background map consists of 32 tiles
; -> [guess_hints]
; <- [BKG_LOC_9800]
; Update the character hint objects
update_hint_markings:
; calculate the
ld hl, BKG_LOC_9800 + 32*20 + 6
ld de, guess_hints
ld hl, BKG_LOC_9800 + 20*32 + 6
ld de, guesses_hints
ld b, 6
.loop_outer:
@ -42,13 +30,8 @@ update_hint_markings:
; Saves the obtained hints.
; -> [current_word]
; -> [current_guess]
; -> [guess]
; Update the hint data
mark_hints:
; To get the beginning of the clues,
; you can use the end of the guessing attempts
call get_guess_offset
push hl
pop de
@ -57,7 +40,6 @@ mark_hints:
ld b, 0
.loop:
; update the hints char by char
ld a, [de]
call hint_for_char
ld [hl], a
@ -71,10 +53,9 @@ mark_hints:
; Determines the hint for one character.
; -> [current_word]
; -> a: character value to check
; -> b: position within the guess
; Determines the hint for one character
; <- a: character value to check
; <- b: index within the guess
hint_for_char:
push hl
push bc

97
inc/init.asm

@ -1,18 +1,4 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; █▄░▄█░▄▄▀██▄██▄░▄██▄██░▄▄▀█░███▄██▄▄░█░▄▄▀█▄░▄██▄██▀▄▄▀█░▄▄▀██
;; ██░██░██░██░▄██░███░▄█░▀▀░█░███░▄█▀▄██░▀▀░██░███░▄█░██░█░██░██
;; █▀░▀█▄██▄█▄▄▄██▄██▄▄▄█▄██▄█▄▄█▄▄▄█▄▄▄█▄██▄██▄██▄▄▄██▄▄██▄██▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Establish a defined initial state
; Initialize everything for the main game state
; <- [current_state]
; <- [sub_state]
; <- [BKG_POS_X_REGISTER]
; <- [BKG_POS_Y_REGISTER]
; <- [LCD_CONTROL_REGISTER]
; <- [message_objs]
; Initialise everything for the main game state
init_state_menu:
ld a, STATE_MENU
ld [current_state], a
@ -36,17 +22,7 @@ init_state_menu:
; Initialize everything for the help screen
; <- [current_state]
; <- [BKG_POS_X_REGISTER]
; <- [BKG_POS_Y_REGISTER]
; <- [WND_POS_X_REGISTER]
; <- [WND_POS_Y_REGISTER]
; <- [LCD_CONTROL_REGISTER]
; <- [current_word]
; <- [guess]
; <- [guess_hints]
; <- [message_objs]
; Initialise everything for help screen
init_state_help:
ld a, STATE_HELP
ld [current_state], a
@ -69,27 +45,24 @@ init_state_help:
ld a, $70
ld [WND_POS_Y_REGISTER], a
; load the window data
ld hl, window_help
call load_window_map
; Set a fixed and actually impossible game state.
; containing: [current_word], [guess] and [guess_hints]
;
; This is only possible because the memory space of these
; three variables is located directly after each other.
; set a fixed game state
ld hl, current_word
ld de, help_word
ld c, 65
.copy_gamestate:
.set_gamestate:
ld a, [de]
ld [hl], a
inc de
inc hl
dec c
jp nz, .copy_gamestate
jp nz, .set_gamestate
; This is only possible because the memory space of these
; two variables is located directly after each other.
; for the rest we can use the default functions
call update_hint_markings
call update_guess_objects
@ -103,21 +76,7 @@ init_state_help:
; Initialize everything for the main game state
; <- [current_state]
; <- [BKG_POS_X_REGISTER]
; <- [BKG_POS_Y_REGISTER]
; <- [WND_POS_X_REGISTER]
; <- [WND_POS_Y_REGISTER]
; <- [LCD_CONTROL_REGISTER]
; <- [current_word]
; <- [current_guess]
; <- [current_char]
; <- [guess]
; <- [guess_hints]
; <- [selected_letter_x]
; <- [selected_letter_y]
; <- [message_objs]
; Initialise everything for the main game state
init_state_game:
; set the current state
ld a, STATE_GAME
@ -139,22 +98,22 @@ init_state_game:
; set the window position
ld a, 3
ld [WND_POS_X_REGISTER], a
ld a, $68
ld a, $70
ld [WND_POS_Y_REGISTER], a
; load the window data
ld hl, window_game
call load_window_map
; initialize some more variables
; initialise some more variables
ld a, 0
ld [selected_letter_x], a
ld [selected_letter_y], a
ld [current_guess], a
ld [current_char], a
.reset_guess_attempts
ld hl, guess_attempts
.reset_guesses
ld hl, guesses
ld a, NULL
ld d, 30
.loop1:
@ -163,7 +122,7 @@ init_state_game:
jp nz, .loop1
.reset_hints
ld hl, guess_hints
ld hl, guesses_hints
ld a, TILE_WHITE
ld d, 30
.loop2:
@ -181,9 +140,7 @@ init_state_game:
; Switches to the state when the player has lost
; <- [current_state]
; <- [message_objs]
; Switches to the lost state
init_state_lost:
ld a, STATE_LOST
ld [current_state], a
@ -192,9 +149,7 @@ init_state_lost:
; Switches to the state when the player has won
; <- [current_state]
; <- [message_objs]
; Switches to the won state
init_state_won:
ld a, STATE_WON
ld [current_state], a
@ -203,10 +158,7 @@ init_state_won:
; Initialize the DMG color palettes
; <- [PALETTE_BKG_REGISTER]
; <- [PALETTE_OBJ0_REGISTER]
; <- [PALETTE_OBJ1_REGISTER]
; Initialise the DMG color palettes
init_palettes:
ld a, %10010011
ld [PALETTE_BKG_REGISTER], a
@ -216,9 +168,7 @@ init_palettes:
; Copy the tile data into the vram
; -> [tiles]
; <- [TLS_LOC_8000]
; Load the tile data into the vram
load_tiles:
ld bc, tiles_start
ld hl, TLS_LOC_8000
@ -239,9 +189,7 @@ load_tiles:
; Copy the background map into the vram
; -> [background]
; <- [BKG_LOC_9800]
; Load the background map into the vram
load_background_map:
ld bc, background
ld hl, BKG_LOC_9800
@ -262,13 +210,12 @@ load_background_map:
; Copy a window map into the vram
; -> hl: address of the desired window map
; <- [WND_LOC_9C00]
; Load the window map into the vram
; -> hl
load_window_map:
ld bc, WND_LOC_9C00
ld d, 32
ld e, 5
ld e, 4
.loop:
ld a, [hl]

47
inc/input.asm

@ -1,14 +1,6 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; █▄░▄█░▄▄▀█▀▄▄▀█░██░█▄░▄██
;; ██░██░██░█░▀▀░█░██░██░███
;; █▀░▀█▄██▄█░█████▄▄▄██▄███
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Handles the player inputs
; Read the current input state
; <- b: current keystates
; <- c: changed keys since last read
; -> b: current keystates
; -> c: changed keys since last read
read_input:
di
@ -50,8 +42,7 @@ read_input:
; React to input within the menu
; -> c: the changed keystate
; React on input within the menu
handle_input_menu:
.check_movement:
ld a, c
@ -63,11 +54,9 @@ handle_input_menu:
ld [sub_state], a
and a, STATE_MENU_START
jp z, .switch_to_help
.switch_to_start
call show_message_menu_start
jp .check_confirm
.switch_to_help
call show_message_menu_help
jp .check_confirm
@ -90,8 +79,6 @@ handle_input_menu:
; React to input within the help screen
; -> c: the changed keystate
handle_input_help:
ld a, c
and a, INPUT_START
@ -102,10 +89,8 @@ handle_input_help:
; React to input within the main game
; -> c: the changed keystate
; <- [selected_letter_x]
; <- [selected_letter_y]
; React on input within the main game
; <- c: changed keys since last read
handle_input_game:
ld a, [selected_letter_x]
ld d, a
@ -193,26 +178,22 @@ handle_input_game:
; React to input after a game round,
; no matter if won or lost
; -> c: the changed keystate
; React on input after a game round
handle_input_after:
ld a, c
and a, INPUT_START
jp z, .nothing
call init_state_game
call clear_message
.nothing
ret
; Select a character and add it to the current guess
; Add a character to the guess
; -> d: x position of the cursor
; -> e: y position of the cursor
; <- [current_guess]
; <- [current_char]
; <- [guess_attempts]
select_letter:
push hl
push af
@ -235,7 +216,7 @@ select_letter:
jp .return
.normal_letter:
ld hl, guess_attempts
ld hl, guesses
ld a, [current_guess]
ld b, 5
call multiply_ab
@ -262,17 +243,14 @@ select_letter:
; Delete the last entered letter
; <- [current_guess]
; <- [current_char]
; <- [guess_attempts]
; Delete the last letter
delete_letter:
push hl
push af
push bc
push de
ld hl, guess_attempts
ld hl, guesses
ld a, [current_guess]
ld b, 5
call multiply_ab
@ -302,9 +280,6 @@ delete_letter:
; Update the object data for the alphabet cursor
; -> [selected_letter_x]
; -> [selected_letter_y]
; <- [obj_selected_letter]
update_cursor_objects:
ld hl, obj_selected_letter

17
inc/interrupts.asm

@ -1,12 +1,4 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; █▄░▄█░▄▄▀█▄░▄█░▄▄█░▄▄▀█░▄▄▀█░██░█▀▄▄▀█▄░▄█░▄▄██
;; ██░██░██░██░██░▄▄█░▀▀▄█░▀▀▄█░██░█░▀▀░██░██▄▄▀██
;; █▀░▀█▄██▄██▄██▄▄▄█▄█▄▄█▄█▄▄██▄▄▄█░█████▄██▄▄▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Handle hardware interrupts
; Interrupt handler for the vertical blanking period
; Interrupt handler for the vertical blanking
int_vblank:
push af
ld a, 1
@ -19,10 +11,7 @@ int_vblank:
; Interrupt handler for the timer
;
; Getting random numbers is hard, here we read the vertical position of the scanline,
; save them periodically and use them to get something. This approach works,
; but is not unbalanced because the y position only goes from 0 to 153.
; To get around this, two values are stored as in a shift register
; and not all bits of this are used when determining the random number.
; save them periodically and use them to get something.
int_timer:
push hl
push af
@ -37,7 +26,7 @@ int_timer:
ld hl, LCD_POSITION_REGISTER
ld a, [hl]
; save the old and new number
; save the new number
ld hl, random_number
ld [hl+], a
ld a, d

8
inc/math.asm

@ -1,11 +1,3 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▀▄░█░▄▄▀█▄░▄█░█████
;; ██░█░█░█░▀▀░██░██░▄▄░██
;; ██░███░█▄██▄██▄██▄██▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Mathematical functions
; Multiply a and b
; -> a, b
; <- a

38
inc/messages.asm

@ -1,16 +1,4 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▀▄░█░▄▄█░▄▄█░▄▄█░▄▄▀█░▄▄▄█░▄▄█░▄▄██
;; ██░█░█░█░▄▄█▄▄▀█▄▄▀█░▀▀░█░█▄▀█░▄▄█▄▄▀██
;; ██░███░█▄▄▄█▄▄▄█▄▄▄█▄██▄█▄▄▄▄█▄▄▄█▄▄▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; To give the user specific information, short messages could be displayed.
;; These are either permanent or can be provided with a timer.
;; Some of these messages are also used for highlighting in the menu.
; Highlights the menu entry "start game"
; <- de: message address
; <- b: message timeout
show_message_menu_start:
ld de, message_menu_start
ld b, 0
@ -20,8 +8,6 @@ show_message_menu_start:
; Highlights the menu entry "how it works"
; <- de: message address
; <- b: message timeout
show_message_menu_help:
ld de, message_menu_help
ld b, 0
@ -31,19 +17,15 @@ show_message_menu_help:
; Inform that the current guess is not in the dictionary
; <- de: message address
; <- b: message timeout
show_message_unknown:
ld de, message_unknown
ld b, 180 ; three seconds
ld b, 180
call show_message
ret
; Inform about the users victory
; <- de: message address
; <- b: message timeout
show_message_won:
ld de, message_won
ld b, 0
@ -53,8 +35,6 @@ show_message_won:
; Inform about the users loss
; <- de: message address
; <- b: message timeout
show_message_lost:
ld de, message_lost
ld b, 0
@ -77,11 +57,9 @@ ENDR
; Generic function to present a message to the user
; -> de: message address
; -> b: message timeout
; <- [obj_message_letters]
; <- [message_timeout]
; Displays a message to the user
; <- de
; <- b
show_message:
push hl
push bc
@ -107,10 +85,7 @@ show_message:
; Checks if the current message has expired and removes it when necessary
; -> [message_timeout]
; <- [message_timeout]
; <- [obj_message_letters]
; Checks if the current message has expired
check_message_timeout:
ld a, [message_timeout]
cp a, 0
@ -128,8 +103,7 @@ check_message_timeout:
; Clears the current message by overwriting it with NULL values
; <- [obj_message_letters]
; Clears the current message by overwriting with nothing
clear_message:
ld de, message_clear
ld b, 0

22
inc/oam.asm

@ -1,32 +1,18 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░▄▄▄░█░▄▄▀███▄█░▄▄█▀▄▀█▄░▄███░▄▄▀█▄░▄█▄░▄█░▄▄▀██▄██░▄▄▀█░██░█▄░▄█░▄▄████░▄▀▄░█░▄▄█░▄▀▄░█▀▄▄▀█░▄▄▀█░██░██
;; ██░███░█░▄▄▀███░█░▄▄█░█▀██░████░▀▀░██░███░██░▀▀▄██░▄█░▄▄▀█░██░██░██░▄▄████░█░█░█░▄▄█░█▄█░█░██░█░▀▀▄█░▀▀░██
;; ██░▀▀▀░█▄▄▄▄█░▀░█▄▄▄██▄███▄████░██░██▄███▄██▄█▄▄█▄▄▄█▄▄▄▄██▄▄▄██▄██▄▄▄████░███░█▄▄▄█▄███▄██▄▄██▄█▄▄█▀▀▀▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Contains functions to manage the object attribute memory (OAM).
;; We do not write to this area directly, but use a copy in the working memory.
;; During a v-blank, the OAM is updated using direct memory access (DMA).
;; Note: the Game Boy can only handle a total of 40 objects, and only 10 can be displayed on a line.
; Fill the whole OAM copy with zero to prevent artifacts
; <- [objects]
; Fill the whole oam copy with zero to prevent artifacts
init_oam_copy:
ld b, 160
ld a, 0
ld hl, obj_start
.copy_loop
.zero_loop
ld [hl+], a
dec b
jp nz, .copy_loop
jp nz, .zero_loop
ret
; Update the OAM via DMA
; -> [objects]
; <- [OAM]
; Write into OAM via DMA
update_oam:
; start DMA
ld a, $c0

113
inc/search.asm

@ -0,0 +1,113 @@
; Compresses the current guess to the same format as the dictionary
; <- bc
; <- de
compress_guess:
call get_guess_offset
; first byte
ld a, [hl+] ; letter 1
and %00111111
sla a
sla a
ld b, a
ld a, [hl] ; letter 2
and %00110000
sra a
sra a
sra a
sra a
add a, b
ld d, a
; second byte
ld a, [hl+] ; letter 2
and %00001111
sla a
sla a
sla a
sla a
ld b, a
ld a, [hl] ; letter 3
and %00111100
sra a
sra a
add a, b
ld e, a
push de
; third byte
ld a, [hl+] ; letter 3
and %00000011
sla a
sla a
sla a
sla a
sla a
sla a
ld b, a
ld a, [hl+] ; letter 4
and %00111111
add a, b
ld d, a
; fourth byte
ld a, [hl] ; letter 5
and %00111111
sla a
sla a
ld e, a
pop bc
ret
; Try to find the guess within the dictionary
; -> bc
; -> de
; <- a
find_guess:
ld hl, dictionary
.loop:
; check if the end of the dictionary is reached
push bc
ld bc, dictionary_end
ld a, h
cp a, b
jp nz, .not_eof
ld a, l
cp a, c
jp nz, .not_eof
pop bc
jp .return
.not_eof:
pop bc
ld a, [hl+]
cp a, b
jp nz, .add3
ld a, [hl+]
cp a, c
jp nz, .add2
ld a, [hl+]
cp a, d
jp nz, .add1
ld a, [hl+]
cp a, e
jp nz, .add0
ld a, 1
ret
.add3:
inc hl
.add2:
inc hl
.add1:
inc hl
.add0:
jp .loop
.return:
ld a, 0
ret

22
inc/wincondition.asm

@ -1,17 +1,4 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░███░██▄██░▄▄▀███▀▄▀█▀▄▄▀█░▄▄▀█░▄▀██▄██▄░▄██▄██▀▄▄▀█░▄▄▀██
;; ██░█░█░██░▄█░██░███░█▀█░██░█░██░█░█░██░▄██░███░▄█░██░█░██░██
;; ██▄▀▄▀▄█▄▄▄█▄██▄████▄███▄▄██▄██▄█▄▄██▄▄▄██▄██▄▄▄██▄▄██▄██▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; Functions to be able to detect the end of the game.
; Check if the guess attempt is valid
; -> [guess]
; -> [current_guess]
; -> [current_char]
; -> [obj_message]
; <- a: whether the test is valid or not
; Check if the guess is valid
check_guess:
push hl
push af
@ -27,6 +14,7 @@ check_guess:
; and then search after it.
call compress_guess
call find_guess
cp a, 1
jp nz, .is_invalid
@ -68,10 +56,8 @@ check_guess:
; Check if guessed correctly and therefore won
; -> [current_word]
; -> [current_guess]
; <- a: whether it is correct or not
; Check the win conditions
; <- a
check_win:
call get_guess_offset
ld bc, current_word

1
maps/window-game.asm

@ -1,5 +1,4 @@
; Window map while in game
DB $1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$01,$1b,$02,$1b,$03,$1b,$04,$1b,$05,$1b,$06,$1b,$07,$1b,$08,$1b,$09,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$0a,$1b,$0b,$1b,$0c,$1b,$0d,$00,$0e,$1b,$0f,$1b,$10,$1b,$11,$1b,$12,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$13,$1b,$14,$1b,$15,$1b,$16,$1b,$17,$1b,$18,$1b,$19,$1b,$1a,$1b,$1e,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b

8
maps/window-help.asm

@ -1,6 +1,6 @@
; Window map while within help
DB $1b,$1b,$13,$14,$01,$12,$14,$1b,$1b,$0e,$05,$17,$1b,$07,$01,$0d,$05,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$13,$05,$0c,$05,$03,$14,$1b,$03,$08,$05,$03,$0b,$1b,$17,$0f,$12,$04,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$02,$22,$01,$1b,$1b,$1b,$1b,$04,$05,$22,$13,$05,$0c,$05,$03,$14,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$13,$14,$01,$12,$14,$1b,$22,$0e,$05,$17,$1b,$07,$01,$0d,$05,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$13,$05,$0c,$05,$03,$14,$22,$03,$08,$05,$03,$0b,$1b,$17,$0f,$12,$04,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b
DB $1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b,$1b

17
tiles/sign-arrow.asm

@ -1,17 +0,0 @@
; Slash
DB %00000000, \
%00000000, \
%00000000, \
%00000000, \
%00001000, \
%00001000, \
%00000100, \
%00000100, \
%01111110, \
%01111110, \
%00000100, \
%00000100, \
%00001000, \
%00001000, \
%00000000, \
%00000000

17
tiles/sign-slash.asm

@ -0,0 +1,17 @@
; Slash
DB %00000000, \
%00000000, \
%00000010, \
%00000010, \
%00000100, \
%00000100, \
%00001000, \
%00001000, \
%00010000, \
%00010000, \
%00100000, \
%00100000, \
%01000000, \
%01000000, \
%10000000, \
%10000000, \

121
wordle.asm

@ -1,15 +1,5 @@
;; ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
;; ██░███░█▀▄▄▀█░▄▄▀█░▄▀█░██░▄▄██
;; ██░█░█░█░██░█░▀▀▄█░█░█░██░▄▄██
;; ██▄▀▄▀▄██▄▄██▄█▄▄█▄▄██▄▄█▄▄▄██
;; ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
;; The famous word puzzle
; Include hardware-specific constants
; used all over the code.
include "inc/constants.asm"
;; Game specific constants
; Game states
STATE_MENU EQU %00000001
@ -32,28 +22,28 @@ TILE_ENTER EQU $1e
TILE_RIGHT EQU $1f
TILE_MISPLACED EQU $20
TILE_WRONG EQU $21
TILE_ARROW EQU $22
TILE_SLASH EQU $22
; Vertical blanking interrupt starting address
SECTION "ENTRY_VBLANK", ROM0[$0040]
jp int_vblank ; used for game logic updates
jp int_vblank
; LCDC status interrupt starting address
SECTION "ENTRY_LCDCS", ROM0[$0048]
reti ; we don't use this interrupt
reti
; Timer overflow interrupt starting address
SECTION "ENTRY_TIMER", ROM0[$0050]
jp int_timer ; used for generating randomness
jp int_timer
; Serial transfer completion interrupt starting address
SECTION "ENTRY_SERIAL", ROM0[$0058]
reti ; we don't use this interrupt
reti
; Program starting address
@ -64,8 +54,7 @@ SECTION "ENTRY_START", ROM0[$0100]
SECTION "MAIN", ROM0[$0150]
main:
; turn the screen off until everything is initialized,
; or wild glitches will appear
; turn the screen off until everything is initialised
ld a, DISPLAY_OFF
ld [LCD_CONTROL_REGISTER], a
ld [LCD_STATUS_REGISTER], a
@ -110,6 +99,7 @@ main_loop:
.within_vblank:
call update_oam
call read_input
ld a, [current_state]
.in_menu:
@ -166,6 +156,7 @@ include "inc/dict.asm"
include "inc/input.asm"
include "inc/guess.asm"
include "inc/hints.asm"
include "inc/search.asm"
include "inc/messages.asm"
include "inc/wincondition.asm"
include "inc/interrupts.asm"
@ -174,10 +165,7 @@ include "inc/interrupts.asm"
;; Game data
SECTION "DATA0", ROM0[$1000]
;; Tiles
; Small 8x8 pixel images, which are addressable
; and will be used for sprite maps and objects.
; Tiles
tiles_start:
tile_null:
include "tiles/plain-null.asm"
@ -197,16 +185,15 @@ tile_misplaced:
include "tiles/sign-misplaced.asm"
tiles_wrong:
include "tiles/sign-wrong.asm"
tiles_arrow:
include "tiles/sign-arrow.asm"
tiles_slash:
include "tiles/sign-slash.asm"
tiles_logo:
include "tiles/logo.asm"
tiles_end:
;; Maps
; Each map defines a grid of tile addresses,
; which combined will form the desired image.
; Maps
maps_start:
background:
include "maps/background.asm"
@ -216,20 +203,13 @@ window_game:
include "maps/window-game.asm"
maps_end:
;; Dictionary
; A list of known words to choose from.
dictionary:
incbin "dict/en.dat"
dictionary_end:
DB
;; Help data
; The help screen will reuse the normal
; game mechanics to provide a manual
; how to play this game.
; Now following is hard coded-data
; to provide a set of information.
; Help data
help_word:
DB $12, $09, $07, $08, $14 ; right
@ -250,11 +230,7 @@ help_guess_hints:
DB TILE_WHITE, TILE_WHITE, TILE_WHITE, TILE_WHITE, TILE_WHITE
;; Messages
; This game uses a message system to
; provide feedback to the user.
; For this, nine objects are reserved
; and can be used at will.
; Messages
message_clear:
DB $00, $00, $00, $00
DB $00, $00, $00, $00
@ -289,41 +265,41 @@ message_menu_help:
DB $90, $80, 25, OBJ_ATTR_PALETTE1 ; Y
message_unknown:
DB $78, $38, 21, OBJ_ATTR_PALETTE1 ; U
DB $78, $40, 14, OBJ_ATTR_PALETTE1 ; N
DB $78, $48, 11, OBJ_ATTR_PALETTE1 ; k
DB $78, $50, 14, OBJ_ATTR_PALETTE1 ; N
DB $78, $58, 15, OBJ_ATTR_PALETTE1 ; O
DB $78, $60, 23, OBJ_ATTR_PALETTE1 ; W
DB $78, $68, 14, OBJ_ATTR_PALETTE1 ; N
DB $74, $38, 21, OBJ_ATTR_PALETTE1 ; U
DB $74, $40, 14, OBJ_ATTR_PALETTE1 ; N
DB $74, $48, 11, OBJ_ATTR_PALETTE1 ; k
DB $74, $50, 14, OBJ_ATTR_PALETTE1 ; N
DB $74, $58, 15, OBJ_ATTR_PALETTE1 ; O
DB $74, $60, 23, OBJ_ATTR_PALETTE1 ; W
DB $74, $68, 14, OBJ_ATTR_PALETTE1 ; N
DB $00, $00, 0, 0
DB $00, $00, 0, 0
message_won:
DB $78, $38, 25, OBJ_ATTR_PALETTE1 ; Y
DB $78, $40, 15, OBJ_ATTR_PALETTE1 ; O
DB $78, $48, 21, OBJ_ATTR_PALETTE1 ; U
DB $78, $58, 23, OBJ_ATTR_PALETTE1 ; W
DB $78, $60, 15, OBJ_ATTR_PALETTE1 ; O
DB $78, $68, 14, OBJ_ATTR_PALETTE1 ; N
DB $74, $38, 25, OBJ_ATTR_PALETTE1 ; Y
DB $74, $40, 15, OBJ_ATTR_PALETTE1 ; O
DB $74, $48, 21, OBJ_ATTR_PALETTE1 ; U
DB $74, $58, 23, OBJ_ATTR_PALETTE1 ; W
DB $74, $60, 15, OBJ_ATTR_PALETTE1 ; O
DB $74, $68, 14, OBJ_ATTR_PALETTE1 ; N
DB $00, $00, 0, 0
DB $00, $00, 0, 0
DB $00, $00, 0, 0
message_lost:
DB $78, $30, 9, OBJ_ATTR_PALETTE1 ; I
DB $78, $38, 20, OBJ_ATTR_PALETTE1 ; T
DB $78, $40, 19, OBJ_ATTR_PALETTE1 ; S
DB $78, $50, 0, OBJ_ATTR_PALETTE1 ; guess[0]
DB $78, $58, 0, OBJ_ATTR_PALETTE1 ; guess[1]
DB $78, $60, 0, OBJ_ATTR_PALETTE1 ; guess[2]
DB $78, $68, 0, OBJ_ATTR_PALETTE1 ; guess[3]
DB $78, $70, 0, OBJ_ATTR_PALETTE1 ; guess[4]
DB $74, $30, 9, OBJ_ATTR_PALETTE1 ; I
DB $74, $38, 20, OBJ_ATTR_PALETTE1 ; T
DB $74, $40, 19, OBJ_ATTR_PALETTE1 ; S
DB $74, $50, 0, OBJ_ATTR_PALETTE1 ; guess[0]
DB $74, $58, 0, OBJ_ATTR_PALETTE1 ; guess[1]
DB $74, $60, 0, OBJ_ATTR_PALETTE1 ; guess[2]
DB $74, $68, 0, OBJ_ATTR_PALETTE1 ; guess[3]
DB $74, $70, 0, OBJ_ATTR_PALETTE1 ; guess[4]
DB $00, $00, 0, 0
;; Address reservations within the working ram
; Address reservations within the working ram
SECTION "RAM", WRAM0
; The first 160 Bytes are reserved for a copy
; of the OAM data, which will be updated via DMA.
@ -343,7 +319,7 @@ obj_dma_padding:
vblank_flag:
DB
; Saves the current random number consisting of 16 bits
; Saves the current 16bit random number
random_number:
DS 2
@ -351,19 +327,19 @@ random_number:
input_state:
DB
; Saves the current game state (see STATE constants)
; Saves the current game state
current_state:
DB
; Saves the subordinate state (see STATE constants)
; Saves the state in the submenu
sub_state:
DB
; Number of the current guess (0..5)
; Number of the current guess
current_guess:
DB
; Position within the current guess (0..4)
; Position within the current guess
current_char:
DB
@ -372,22 +348,21 @@ current_word:
DS 5
; The guess attempts
guess_attempts:
guesses:
DS 30
; The corresponding hints
guess_hints:
guesses_hints:
DS 30
; Message timeout (remaining number of frames)
; Message timeout
message_timeout:
DB
; Horizontal position of the letter selection within the alphabet grid
; Horizontal position of the letter selection
selected_letter_x:
DB
; Vertical position of the letter selection within the alphabet grid
; Vertical position of the letter selection
selected_letter_y:
DB

BIN
wordle.gb

Binary file not shown.
Loading…
Cancel
Save