Sokoban / Sokoban (1).s
Sokoban (1).s
Raw
#Pseudo random number generation citation
#Marsaglia, George. Xorshift RNGs, The Florida State University, 2003. Retrieved from https://www.jstatsoft.org/article/view/v008i14/916
.data
welcomemsg: .string "Welcome to Sokoban!\n"
gridmsg:    .string "Please enter the grid size:\n"
invalidmsg: .string "Invalid input, please try again:\n"
winmsg:     .string "Congratulations! You completed the level!\n"
movemsg:    .string "Please enter a move:\n"
invalidmove: .string "Invalid move, please try again:\n"
quitmsg:     .string "Type <q> to exit or <r> to restart game:\n"
.align 4 
invalidsize:  .string "Grid too big, please enter a new value.\n"
.align 4 
seed:        .word 12345678  # Initial seed value
gridsize:   .byte 8,8
character:  .byte 0,0
box:        .byte 0,0
target:     .byte 0,0
gamestate:  .byte 0

# Symbols for rendering
wall_sym:   .byte '#'
player_sym: .byte '@'
box_sym:    .byte 'D'
target_sym: .byte 'x'
empty_sym:  .byte ' '

.text
.globl _start

_start:
	j main_loop
	
generate_boxlocx:
	#initialize x location for box:
	la t0, gridsize
	lb t2, 0(t0)
	addi t2, t2, -1
	
	mv a0, t2
	jal ra, xorshift
	la t1, box
	sb a0, 0(t1)
	
	li t3, 0
	beq a0, t3, generate_boxlocx
	
	li t3, 1
	beq a0, t3, check_corners
	
	la t0, gridsize
	lb t3, 1(t0)          #grid height
	addi t3, t3, -2       #bottom corners   
	beq a0, t3, check_corners
	
	j generate_boxlocy
	
check_corners:
	#initialize y location for box:
	la t0, gridsize
	lb t2, 0(t0)    #get width
	addi t2, t2, -1
	
	mv a0, t2
	jal ra, xorshift
	la t1, box
	sb a0, 1(t1)
	
	li t3, 0
	beq a0, t3, check_corners #check if y=0
	
	li t3, 1
	beq a0, t3, check_corners #check if at (1,1) or (height-2, 1)which is corner
	
	la t0, gridsize
	lb t2, 0(t0)    #get width
	addi t2, t2, -2
	beq a0, t2, check_corners #check if at (1,width-2) or (height-2, width-2) which is corner
	
	j check_beside_wall
	
generate_boxlocy:
	#initialize y location for box:
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1
	
	mv a0, t2
	jal ra, xorshift
	la t1, box
	sb a0, 1(t1)
	
	li t3, 0
	beq a0, t3, generate_boxlocy
	
	j check_beside_wall
	
check_beside_wall:
	#check if the box is beside a wall so the target must be on the same wall to be solveable
	 # Load grid size (width and height)
    la t0, gridsize
    lb t2, 0(t0)         # Load grid width
    addi t2, t2, -2      # Subtract 2 to get grid width limit
    lb t3, 1(t0)         # Load grid height
    addi t3, t3, -2      # Subtract 2 to get grid height limit
    
    # Load box coordinates
    la t1, box
    lb t4, 0(t1)         # box x
    lb t5, 1(t1)         # box y

    # Check if the box is against the left or right wall
    li t6, 1
    beq t4, t6, target_on_x_wall    # if x = 1 (top wall), place target on x wall
    beq t4, t2, target_on_x_wall    # if x = grid width-2 (bottom wall), place target on x wall

    # Check if the box is against the top or bottom wall
    li t6, 1
    beq t5, t6, target_on_y_wall    # if y = 1 (left wall), place target on y wall
    beq t5, t3, target_on_y_wall    # if y = grid height-2 (right wall), place target on y wall

    # if the box is not near a wall, generate target randomly
    j generate_target_locx
	
target_on_x_wall:
	la t1, box
    lb t2, 0(t1)         # box x
    lb t3, 1(t1)         # box y
	
	la t4, target
	lb t5, 0(t4)
	lb t6, 1(t4)
	
	sb t2, 0(t4)         # target x location must be same as box x loc
	
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t2, target              # Load target address
	sb a0, 1(t2)            # store target y coord 
	
	la t2, box           # Load box address
	lb t0, 1(t2)              # Load box y coord
	
	li t3, 0
	beq a0, t0, target_on_x_wall    
	beq a0, t3, target_on_x_wall
	
	j generate_charlocx
	
target_on_y_wall:
	la t1, box
    lb t2, 0(t1)         # Box x-coordinate
    lb t3, 1(t1)         # Box y-coordinate
	
	la t4, target
	lb t5, 0(t4)
	lb t6, 1(t4)
	
	sb t3, 1(t4)         # target x location must be same as box x loc
	
	la t0, gridsize
	lb t2, 0(t0)
	addi t2, t2, -1
	
	#initialize x location for box:
	mv a0, t2
	jal ra, xorshift
	la t2, target              # Load target address
	sb a0, 0(t2)            # store target x coord 
	
    la t2, box           # Load box address
	lb t0, 0(t2)            # Load target x coord
	
	li t3, 0
	beq a0, t0, target_on_y_wall
	beq a0, t3, target_on_y_wall
	
	j generate_charlocx
	
generate_target_locx:
	la t0, gridsize
	lb t2, 0(t0)
	addi t2, t2, -1
	
	#initialize x location for box:
	mv a0, t2
	jal ra, xorshift
	la t2, target              # Load target address
	sb a0, 0(t2)            # store target x coord 
	
    la t4, box           # Load box address
	lb t0, 0(t4)            # Load box x coord
	
	li t3, 0
	beq a0, t3, generate_target_locx
	beq a0, t0, generate_target_diffy
	
	j generate_target_locy
	
generate_target_diffy:
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t2, target              # Load target address
	sb a0, 1(t2)            # store target y coord 
	
	la t2, box           # Load box address
	lb t0, 1(t2)              # Load box y coord
	
	li t3, 0
	beq a0, t3, generate_target_diffy
	beq a0, t0, generate_target_diffy
	
	j generate_charlocx
	
generate_target_locy:
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t2, target              # Load target address
	sb a0, 1(t2)            # store target y coord 
	
	li t3, 0
	beq a0, t3, generate_target_locy

generate_charlocx:
	la t0, gridsize
	lb t2, 0(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t0, character        #t0 = character address
	sb a0, 0(t0)
	
    la t1, target          # t1 = target address
	la t2, box             # t2 = box address
	lb t3, 0(t1)           # t3 = target[0]
	lb t4, 0(t2)           #t4 = box[0]
	
	li t5, 0
	beq a0, t5, generate_charlocx
	beq a0, t3, generate_chardifftargety
	beq a0, t4, generate_chardiffboxy

	j generate_charlocy

generate_chardifftargety:
	
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t0, character        #t0 = character address
	sb a0, 1(t0)
	
    la t1, target          # t1 = target address
	la t2, box             # t2 = box address
	lb t3, 1(t1)           # t3 = target[1]
	lb t4, 1(t2)           #t4 = box[1]
	
	li t5, 0
	beq a0, t5, generate_chardifftargety  #if 0, regenerate
	beq a0, t3, generate_chardifftargety  #if same y as target, regenerate
	beq a0, t4, generate_chardiffboxx  #if same y as box, regenerate
	
	j main_loop

generate_chardiffboxx:
	la t0, target
	lb t1, 0(t0)
	la t2, box             # t2 = box address
	lb t6, 0(t2)           # t6 = box[0]
	beq t1, t6, generate_charlocx         #if same target x as box, then regenerate x
	j main_loop
	
generate_chardiffboxy:
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1

	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t0, character        #t0 = character address
	sb a0, 1(t0)
	
    la t1, target          # t1 = target address
	la t2, box             # t2 = box address
	lb t3, 1(t1)           # t3 = target[1]
	lb t4, 1(t2)           #t4 = box[1]
	
	li t5, 0
	beq a0, t5, generate_chardiffboxy  #if 0, regenerate
	beq a0, t4, generate_chardiffboxy  #if same y as box, regenerate
	beq a0, t3, generate_chardifftargetx  #if same y as target, regenerate
	
	j main_loop

generate_chardifftargetx:
	la t0, box
	lb t1, 0(t0)
	la t2, target             # t2 = box address
	lb t6, 0(t2)              # t6 = box[0]
	beq t1, t6, generate_charlocx         #if same target x as box, then regenerate x
	j main_loop
	
generate_charlocy:
	la t0, gridsize
	lb t2, 1(t0)
	addi t2, t2, -1
	
	#initialize y location for box:
	mv a0, t2
	jal ra, xorshift
	la t0, character        #t0 = character address
	sb a0, 1(t0)
	
	la t1, target          # t1 = target address
	la t2, box             # t2 = box address
	lb t3, 1(t1)           # t3 = target[1]
	lb t4, 1(t2)           #t4 = box[1]
	
	li t5, 0
	beq a0, t3, generate_charlocy
	beq a0, t4, generate_charlocy
	beq a0, t5, generate_charlocy
	
	j main_loop

print_grid:
	la t0, gridsize
    lb t1, 0(t0)      # Grid width
    lb t2, 1(t0)      # Grid height
	
    li s0, 0                  # Row index (i)
    li s1, 0                  # Column index (j)
	
	li a0, 10                 # Load newline character `\n`
    li a7, 11                 # Syscall: write character
    ecall
print_row:
	bge s0, t2, move
	
    # Reset column index at the start of each row
    li s1, 0                  # Set column index to 0
print_col:
	bge s1, t1, newline
	
	beq s0, x0, print_wall
	beq s1, x0, print_wall
	addi t6, t1, -1
	beq s0, t6, print_wall
	beq s1, t6, print_wall
	
check_player_x:
	
	la t3, character
    lb t4, 0(t3)      # Character x-coordinate
    lb t5, 1(t3)      # Character y-coordinate
	beq s1, t4, check_player_y
	j check_target_x
	
check_player_y:	
	beq s0, t5, print_player
	
check_target_x:
	
	la t3, target
    lb t4, 0(t3)      # Character x-coordinate
    lb t5, 1(t3)      # Character y-coordinate
	beq s1, t4, check_target_y
	j check_box_x
	
check_target_y:
	beq s0, t5, print_target

check_box_x:
	la t3, box
    lb t4, 0(t3)      # Box x-coordinate
    lb t5, 1(t3)      # Box y-coordinate
	beq s1, t4, check_box_y
	j print_empty
		
check_box_y:
	beq s0, t5, print_box
	
print_empty:
	li a0, ' '                
    j print_char
print_wall:
	li a0, '#'               
    j print_char
print_player:
	li a0, '@'                
    j print_char
print_box:
	li a0, 'D'                
    j print_char
print_target:
	li a0, 'x'                
    j print_char

print_char:
    # Print the character in a0
    li a7, 11                 # Syscall: write character
    ecall

    # Move to the next column
    addi s1, s1, 1            # Increment column index
    j print_col                # Jump to column loop
    
newline:
    # Print a newline at the end of the row
    li a0, 10                 # Load newline character `\n`
    li a7, 11                 # Syscall: write character
    ecall

    # Move to the next row
    addi s0, s0, 1            # Increment row index
    j print_row                # Jump to row loop
	
main_loop:
    la t0, gamestate      # Load the gamestate address
    lb t1, 0(t0)          # Load the current gamestate
	li t2, 0
	li t3, 2
	li t4, 1
	
    beq t1, t2, main_menu  # If gamestate == 0, go to main menu
    beq t1, t3, game_loop  # If gamestate == 2, continue to game loop
    beq t1, t4, gamewin  # If gamestate == 1 (win) 

    j main_loop           # Continue the main loop
	
main_menu:
	# Display main menu
    li a7, 4
	la a0, welcomemsg
    ecall

    li a7, 4
	la a0, gridmsg
    ecall

    # Get grid size
	li a7, 5
    ecall	
	mv t2, a0 
	
	li t5, 99
	bge t2, t5, invalid_size
	la t3, gridsize
	sb a0, 0(t3)
	sb a0, 1(t3)
    
    # Set the gamestate to ongoing (2)
	la t0, gamestate      # Load the gamestate address
    li t1, 2
    sb t1, 0(t0)          # Set gamestate to 2

    j generate_boxlocx           # Jump back to main loop

invalid_size:
	li a7, 4
	la a0, invalidsize
    ecall
	j main_menu
game_loop:
    j print_grid
	

move:
	la t0, gamestate
	lb t1, 0(t0)          # Load the current gamestate
	li t2, 1
	beq t2, t1, main_loop
	
    li a7, 4
	la a0, movemsg 
    ecall

    # Get move
	li a7, 12
    ecall	
	mv t1, a0 

    # Check if the input is 'w', 'a', 's', or 'd' for movement
    li t2, 'w'
    beq t1, t2, move_up    # If input is 'w', move player up

    li t2, 'a'
    beq t1, t2, move_left  # If input is 'a', move player left

    li t2, 's'
    beq t1, t2, move_down  # If input is 's', move player down

    li t2, 'd'
    beq t1, t2, move_right # If input is 'd', move player right
	
	li t2, 'r'
    beq t1, t2, restart # If input is 'r', restart game
	
	li t2, 'q'
    beq t1, t2, exit # If input is 'q', quit game
    
    j invalid_move           # if not anything then move is invalid

restart:
	j generate_boxlocx
	
gamewin:
    li a0, 10                 # Load newline character `\n`
    li a7, 11                 # Syscall: write character
    ecall
	
	li a7, 4
	la a0, winmsg 
    ecall
	
	li a7, 4
	la a0, quitmsg 
    ecall
	
	# Get input
	li a7, 12
    ecall	
	mv t1, a0 
	
	li t2, 'q'
    beq t1, t2, exit # If input is 'q', quit game
	
	la t0, gamestate
	li t3, 0
	sb t3, 0(t0)
	
	li t2, 'r'
    beq t1, t2, main_loop # If input is 'r', restart game
	
	li a7, 4
	la a0, invalidmsg
    ecall
	
	j gamewin
	
invalid_move:
	li a0, 10                 # Load newline character `\n`
    li a7, 11                 # Syscall: write character
    ecall
	
	li a7, 4
	la a0, invalidmove 
    ecall
	j move
	
move_up:
	la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	lb t2, 1(t0)          #t2 = y coord of player
	
	addi t2, t2, -1
	li t6, 0
	beq t6, t2, invalid_move  #if the player hits the top wall at y = 0
	
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
		
	bne t1, t4, move_player_up  #if player doesnt have a box on top move y loc
	beq t2, t5, checkbox_above  #if player has box on top
	bne t2, t5, move_player_up  #if player doesnt have a box on top move y loc
	
move_player_up:
	la t0, character
	lb t1, 1(t0)          #t1 = y coord of player
	addi t1, t1, -1
	sb t1, 1(t0)          #y coord of player = y coord of player - 1
	
	la t0, gamestate 
    li t1, 2
    sb t1, 0(t0)          # Set gamestate to 2
	
	j check_win           #check if box is now on target
	
checkbox_above:
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
	
	addi t5, t5, -1
	li t6, 0
	beq t6, t5, invalid_move #if the box hits the top wall at y = 0 after the player pushes it
	
	sb t5, 1(t3)         #y coord of box = y coord of box + 1
	
	bne t6, t5, move_player_up  #move player too if move is valid
	
move_down:
	la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	lb t2, 1(t0)          #t2 = y coord of player
	
	addi t2, t2, 1
	la t6, gridsize
	lb s0, 1(t6)          #s0 = grid height
	addi s0, s0, -1
	
	beq s0, t2, invalid_move  #if the player hits the bottom wall at y = grid height - 1
	
	la t3, box
	lb t4, 0(t3)         #t4 = x coord of box 
	lb t5, 1(t3)         #t5 = y coord of box
		
	bne t1, t4, move_player_down  #if player doesnt have a box on top move y loc
	beq t2, t5, checkbox_down  #if player has box on top
	bne t2, t5, move_player_down  #if player doesnt have a box on top move y loc
	
move_player_down:
	la t0, character
	lb t1, 1(t0)          #t1 = y coord of player
	addi t1, t1, 1
	sb t1, 1(t0)          #y coord of player = y coord of player + 1
	
	la t0, gamestate 
    li t1, 2
    sb t1, 0(t0)          # Set gamestate to 2
	
	j check_win           #check if box is now on target
	
checkbox_down:
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
	
	addi t5, t5, 1
	
	la t6, gridsize
	lb s0, 1(t6)          #s0 = grid height
	addi s0, s0, -1
	
	beq s0, t5, invalid_move #if the box hits the bottom wall at y = grid height - 1 after the player pushes it
	
	sb t5, 1(t3)         #y coord of box = y coord of box - 1
	
	j move_player_down  #move player too if move is valid

move_left:
    la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	lb t2, 1(t0)          #t2 = y coord of player
	
	
	addi t1, t1, -1
	li t6, 0
	beq t6, t1, invalid_move  #if the player hits the left wall at x = 0
	
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
		
	bne t2, t5, move_player_left  #if player doesnt have a box on left move x loc
	beq t1, t4, checkbox_left  #if player has box on left 
	bne t1, t4, move_player_left  #if player doesnt have a box on top move x loc
	
move_player_left:
	la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	addi t1, t1, -1
	sb t1, 0(t0)          #y coord of player = x coord of player - 1
	
	la t0, gamestate 
    li t1, 2
    sb t1, 0(t0)          # Set gamestate to 2
	
	j check_win           #check if box is now on target
	
checkbox_left:
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
	
	addi t4, t4, -1
	li t6, 0
	beq t6, t4, invalid_move #if the box hits the left wall at x = 0 after the player pushes it
	
	sb t4, 0(t3)         #y coord of box = y coord of box + 1
	
	j move_player_left  #move player too if move is valid
	
move_right:
    la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	lb t2, 1(t0)          #t2 = y coord of player
	
	addi t1, t1, 1
	la t6, gridsize
	lb s0, 0(t6)          #s0 = grid width
	addi s0, s0, -1
	
	beq s0, t1, invalid_move  #if the player hits the bottom wall at x = grid width - 1
	
	la t3, box
	lb t4, 0(t3)         #t4 = x coord of box 
	lb t5, 1(t3)         #t5 = y coord of box
		
	bne t2, t5, move_player_right  #if player doesnt have a box on right move x loc
	beq t1, t4, checkbox_right  #if player has box on right
	bne t1, t4, move_player_right  #if player doesnt have a box on right move x loc

move_player_right:
	la t0, character
	lb t1, 0(t0)          #t1 = x coord of player
	addi t1, t1, 1
	sb t1, 0(t0)          #x coord of player = x coord of player + 1
	
	la t0, gamestate 
    li t1, 2
    sb t1, 0(t0)          # Set gamestate to 2
	
	j check_win           #check if box is now on target
	
checkbox_right:
	la t3, box
	lb t4, 0(t3)
	lb t5, 1(t3)
	
	addi t4, t4, 1
	
	la t6, gridsize
	lb s0, 0(t6)          #s0 = grid width
	addi s0, s0, -1
	
	beq s0, t4, invalid_move #if the box hits the right wall at x = grid width - 1 after the player pushes it
	
	sb t4, 0(t3)         #x coord of box = x coord of box + 1
	
	j move_player_right  #move player too if move is valid
	
check_win:
	la t0, target
	la t1, box
	lb t3, 0(t0) 
	lb t4, 1(t0)
	lb t5, 0(t1)
	lb t6, 1(t1)
	
	bne t3, t5, main_loop    #if x coords dont match then box isnt on target
	bne t4, t6, main_loop    #if y coords dont match then box isnt on target
	
	la t0, gamestate 
    li t1, 1
    sb t1, 0(t0)          # Set gamestate to win = 1
	
	j print_grid
	
exit:
	li t0, 0
	li t1, 0
	li t2, 0
	li t3, 0
	li t4, 0
	li t5, 0
	li t6, 0
	li s0, 0
	li s1, 0
	
    li a7, 10
    ecall
    
     
# Arguments: an integer MAX in a0
# Return: A number from 0 (inclusive) to MAX (exclusive)
xorshift:	
	# Load the MAX from a0
	mv t2, a0
    la t1, seed          # Load address of the seed
    lw t0, 0(t1)        # Load the current seed into t0

    # XORShift algorithm for 32-bit
    slli t3, t0, 13     # t1 = t0 << 13
    xor t0, t0, t3      # t0 ^= t1

    srli t3, t0, 17      # t1 = t0 >> 17
    xor t0, t0, t3      # t0 ^= t1

    slli t3, t0, 5      # t1 = t0 << 5
    xor t0, t0, t3      # t0 ^= t1
	
	# Store the new seed back
    sw t0, 0(t1)        

    # Apply modulus to limit to range 0-MAX
    remu t0, t0, t2      # t0 = t0 % MAX
	
	mv a0, t0

    # Return with random number in a0
    jr ra               # Return to caller