# Author: Kevin Tierney # # Description: This program simulates a Mancala game. # # CONSTANTS # # syscall codes PRINT_INT = 1 PRINT_STRING = 4 READ_INT = 5 EXIT = 10 .data disp_welcome: .asciiz "************************\n** Little Mancala **\n************************\n\n" disp_player1: .asciiz "\n <=== Player 1 <===\n" disp_player2: .asciiz " ===> Player 2 ===>\n\n" disp_board_top_labels: .asciiz " M1 6 5 4 3 2 1 M2\n" disp_board_bottom_labels: .asciiz " M1 8 9 10 11 12 13 M2\n" disp_board_pluses: .asciiz "+----+----+----+----+----+----+----+----+\n" disp_bowl_blank_m1: # This one is for M1 .asciiz "| " disp_bowl_blank_m2: # This one is for M2 .asciiz "| |" disp_bowl_left: # left of the number. Add a space if the number is < 10 .asciiz "| " disp_bowl_right: # right of the number .asciiz " " space: .asciiz " " disp_center_left: # Could require 2nd space .asciiz "+ " disp_center_center: # Could also require trailing space .asciiz " +----+----+----+----+----+----+ " disp_center_right: .asciiz " +\n" player1: .asciiz "Player 1" player2: .asciiz "Player 2" input_query_1: .asciiz ": select a bowl to move from (1-6, -1 to skip turn, -2 to quit):" input_query_2: .asciiz ": select a bowl to move from (8-13, -1 to skip turn, -2 to quit):" error_bowl_num: .asciiz "Illegal bowl number.\n" error_empty: .asciiz "Illegal move, empty bowl.\n" win_postfix: .asciiz " wins!" tie: .asciiz "The game ends in a tie." endl: .asciiz "\n" board: # 1-6,M1,8-13,M2 .word 4,4,4,4,4,4,0,4,4,4,4,4,4,0 .text # this is program code .align 2 # instructions must be on word boundaries .globl main # main is a global label # # Name: MAIN PROGRAM # # Description: Main logic for the program. # # $s0: address of the board # main: jal print_welcome la $s0,board # $s0 = address of the board move $a0,$s0 # Copy $s0 into $a0 jal run_game move $a0,$s0 jal who_is_winning beq $v0,2,m_player2_winning beq $v0,3,m_tie li $v0,PRINT_STRING la $a0,player1 syscall la $a0,win_postfix syscall j m_done_winning m_player2_winning: li $v0,PRINT_STRING la $a0,player2 syscall la $a0,win_postfix syscall j m_done_winning m_tie: li $v0,PRINT_STRING la $a0,tie syscall m_done_winning: # Exit the program li $v0,EXIT syscall # # Name: run_game # Runs the game from start to finish # Inputs: $a0 - Pointer to the board # Outputs: none # run_game: # Save all data to the stack sub $sp,$sp,36 sw $ra,32($sp) sw $s7,28($sp) sw $s6,24($sp) sw $s5,20($sp) sw $s4,16($sp) sw $s3,12($sp) sw $s2,8($sp) sw $s1,4($sp) sw $s0,($sp) move $t0,$a0 # # We will use $s0 - $s9, because none of the other functions touch these registers # $s0 = current turn (0 = player 1, 1 = player 2) # $s1 = board move $s1,$a0 li $s0,0 # Set the first turn to player 1 game_loop: li $s7,0 jal print_game gl_before_print_game: beq $s0,0,gl_print_p1 # Print out "Player 2" li $v0,PRINT_STRING la $a0,player2 syscall li $v0,PRINT_STRING la $a0,input_query_2 syscall j gl_done_printing gl_print_p1: # Print out "Player 1" li $v0,PRINT_STRING la $a0,player1 syscall li $v0,PRINT_STRING la $a0,input_query_1 syscall gl_done_printing: # Read in user input li $v0,READ_INT syscall beq $v0,-1,change_turn beq $v0,-2,game_loop_end move $a0,$s1 move $a1,$s0 move $a2,$v0 jal take_turn # Check the output beq $v0,1,rg_illegal_bowl beq $v0,2,rg_empty_bowl j rg_no_err rg_illegal_bowl: li $v0,PRINT_STRING la $a0,error_bowl_num syscall j gl_before_print_game rg_empty_bowl: li $v0,PRINT_STRING la $a0,error_empty syscall j gl_before_print_game rg_no_err: # Before changing the turn, see if the game is actually over move $a0,$s1 # Move the board pointer into $a0 jal is_game_over bne $v0,$zero,rg_game_over j rg_game_not_over rg_game_over: j game_loop_end rg_game_not_over: change_turn: # Changes the current turn to the other player not $s0,$s0 # First not it sll $s0,$s0,31 # Shift it all the way to the left, to clear out the other bits srl $s0,$s0,31 # Shift back to position 1, leaving us with only a changed value in the least sig bit move $a0,$s1 move $a1,$s0 jal must_skip # Check if the player cannot make a move bne $v0,$zero,change_turn j game_loop game_loop_end: # Restore the whole stack and ra lw $ra,32($sp) lw $s7,28($sp) lw $s6,24($sp) lw $s5,20($sp) lw $s4,16($sp) lw $s3,12($sp) lw $s2,8($sp) lw $s1,4($sp) lw $s0,($sp) add $sp,$sp,36 jr $ra # # Name: is_game_over # Checks whether or not the game is over # Inputs: $a0 - pointer to the board # Outputs: $v0 - 1 if the game is not over, 0 if it is over # is_game_over: move $t0,$a0 # First determine whether or not the game is over # $t1 = M1 value # $t2 = M2 value lw $t1,24($t0) lw $t2,52($t0) add $t1,$t1,$t2 # Add the two mancala bowls together li $t3,48 # If the two bowls add up to 48, the game is over beq $t1,$t3,isgo_game_not_over li $v0,0 j isgo_game_over isgo_game_not_over: li $v0,1 isgo_game_over: jr $ra # # Name: who_is_winning # Checks to see who is currently winning the game # Inputs: $a0 - pointer to the board # Outputs: $v0 - 1 if player 1 won, 2 if player 2 won, 3 if it was a tie # who_is_winning: move $t0,$a0 # $t1 = Value of M1 # $t2 = Value of M2 # $t3 = 1 if $t1 < $t2 (player 2 won) 0 if player 1 won lw $t1,24($t0) lw $t2,52($t0) beq $t1,$t2,wiw_tie slt $t3,$t1,$t2 beq $t3,$zero,wiw_player1 li $v0,2 j wiw_end wiw_player1: li $v0,1 j wiw_end wiw_tie: li $v0,3 wiw_end: jr $ra # # Name: take_turn # Takes the turn for the current player # Inputs: $a0 - pointer to the board, $a1 - the turn # $a2 - user bowl input # Output: $v0 - 0 on success, 1 on failure (illegal bowl number), 2 on failure (empty bowl) # take_turn: move $t0,$a0 move $t1,$a1 move $t2,$a2 # First check that the bowl they picked is valid for that player # $t3 will contain the lower bound, $t4 will contain the upper bound # $t7 is the address of the enemy mancala bowl (M1 or M2). This is set here for later beq $t1,$zero,tt_set_p1 # Set values for player 2 li $t3,8 li $t4,13 add $t7,$t0,24 j tt_done_setting tt_set_p1: li $t3,1 li $t4,6 add $t7,$t0,52 tt_done_setting: # $t5 = is the number greater than the lower bound # $t6 = is the number less than the upper bound # Then, $t5 = $t5 AND $t6 sge $t5,$t2,$t3 sle $t6,$t2,$t4 and $t5,$t5,$t6 beq $t5,$zero,tt_err_ibowl # Error if the bowl is not valid for this player # Get the value of the bowl # Increment $t3 to the bowl location # $t4 now is the value in the bowl mul $t2,$t2,4 # Offset sub $t2,$t2,4 # 0 indexed add $t3,$t0,$t2 lw $t4,($t3) beq $t4,$zero,tt_err_empty # If it is empty, branch off to an error state # Now set the current bowl value to 0 sw $zero,($t3) # Begin dropping stones into bowls # $t8 = maximum value (indicates we have to loop back to $t0) add $t8,$t0,56 tt_bowl_loop: add $t3,$t3,4 # Increment position at start, since our first bowl is the start bowl beq $t3,$t8,tt_bowl_loop_reset # If we reach the maximum value, reset to 0 before continuing beq $t3,$t7,tt_bowl_loop_continue # If we reach the enemy mancala bowl, continue j tt_bowl_loop_no_reset tt_bowl_loop_reset: move $t3,$t0 tt_bowl_loop_no_reset: # Drop a stone in the current bowl. $t6 is our temp value lw $t6,($t3) add $t6,$t6,1 sw $t6,($t3) # Decrement the number of stones left sub $t4,$t4,1 beq $t4,$zero,tt_bowl_loop_end # Exit the loop when there are no more stones tt_bowl_loop_continue: j tt_bowl_loop tt_bowl_loop_end: li $v0,0 j tt_end_err tt_err_ibowl: li $v0,1 j tt_end_err tt_err_empty: li $v0,2 tt_end_err: jr $ra # # Name: must_skip # Checks to see if the current player has to skip their turn or not # Inputs: $a0 - the board, $a1 - the player (0/1) # Outputs: $v0 - 1: the player must skip, 0: the player does not have to skip # must_skip: # $t0 = start # $t1 = end # $t2 = value at current position # $t3 = 1, if a non zero value is found, then it is 0 li $t3,1 move $t0,$a0 beq $a1,$zero,ms_set_p1 # Set for player 2 add $t0,$t0,28 add $t1,$t0,24 j ms_loop ms_set_p1: # Set for player 1 add $t1,$t0,24 ms_loop: lw $t2,($t0) # Load in the current value bne $t2,$zero,ms_found_over_zero add $t0,$t0,4 beq $t0,$t1,ms_end_loop j ms_loop ms_found_over_zero: li $t3,0 ms_end_loop: move $v0,$t3 jr $ra # Name: initialize_board # Initializes the board to: # 1-6,8-13: 4, M1/M2: 0 # Inputs: $a0 - Pointer to the board # Outputs: none # initialize_board: li $t1,0 # $t1 is the loop counter li $t2,4 # Value to store # Loop through each number and set it to 4, and set M1 and M2 to 0 initialize_board_loop: beq $t1,6,initialize_board_setm # If we are setting M1 beq $t1,13,initialize_board_setm # If we are setting M2 sw $t2,($t0) initialize_board_loop_cont: add $t0,$t0,4 # Increment array pointer add $t1,$t1,1 # Increment loop counter bge $t1,14,initialize_board_end_loop j initialize_board_loop initialize_board_setm: # Set M1, M2 sw $zero,($t0) j initialize_board_loop_cont initialize_board_end_loop: jr $ra # Name: print_welcome # Prints the welcome message # Inputs: None # Output: None print_welcome: li $v0,PRINT_STRING la $a0,disp_welcome syscall jr $ra # Name: print_game # Prints the game out onto the screen # Inputs: $a0 - Pointer to the board # Output: None print_game: move $t0,$a0 li $v0,PRINT_STRING la $a0,disp_player1 syscall la $a0,disp_board_top_labels syscall la $a0,disp_board_pluses syscall la $a0,disp_bowl_blank_m1 syscall # $t0 = Address to current number to print out (already assigned above) # $t1 = Less than 10 marker # $t2 = The number # $t3 = Counter add $t0,$t0,20 # Put it up to number 6 li $t3,6 #################### Print out 6 to 1 (in that order) print_game_loop1: li $v0,PRINT_STRING la $a0,disp_bowl_left syscall # Grab the number from memory and increment the offset lw $t2,($t0) sub $t0,$t0,4 # If the number is less than 10, print out a space slt $t1,$t2,10 beq $t1,$zero,print_game_skip_space1 # Print the space li $v0,PRINT_STRING la $a0,space syscall print_game_skip_space1: li $v0,PRINT_INT move $a0,$t2 syscall li $v0,PRINT_STRING la $a0,disp_bowl_right syscall # Decrement counter sub $t3,$t3,1 # Exit the loop when we are done beq $t3,$zero,print_game_loop1_end j print_game_loop1 print_game_loop1_end: li $v0,PRINT_STRING la $a0,disp_bowl_blank_m2 syscall la $a0,endl syscall #################### Print out the middle row li $v0,PRINT_STRING la $a0,disp_center_left syscall # $t0 should be back to 0 now, so we can offset normally lw $t2,28($t0) slt $t1,$t2,10 beq $t1,$zero,print_game_m1_nospace # Branch if we don't need a space li $v0,PRINT_STRING la $a0,space syscall print_game_m1_nospace: li $v0,PRINT_INT move $a0,$t2 syscall li $v0,PRINT_STRING la $a0,disp_center_center syscall # Get M2 lw $t2,56($t0) slt $t1,$t2,10 beq $t1,$zero,print_game_m2_nospace # Branch if we don't need a space li $v0,PRINT_STRING la $a0,space syscall print_game_m2_nospace: li $v0,PRINT_INT move $a0,$t2 syscall li $v0,PRINT_STRING la $a0,disp_center_right syscall la $a0,disp_bowl_blank_m1 syscall ################### 8 to 13 row add $t0,$t0,32 # Put it up to number 8 li $t3,6 # Print out 8 to 13 (in that order) print_game_loop2: li $v0,PRINT_STRING la $a0,disp_bowl_left syscall # Grab the number from memory and increment the offset lw $t2,($t0) add $t0,$t0,4 # If the number is less than 10, print out a space slt $t1,$t2,10 beq $t1,$zero,print_game_skip_space2 # Print the space li $v0,PRINT_STRING la $a0,space syscall print_game_skip_space2: li $v0,PRINT_INT move $a0,$t2 syscall li $v0,PRINT_STRING la $a0,disp_bowl_right syscall # Decrement counter sub $t3,$t3,1 # Exit the loop when we are done beq $t3,$zero,print_game_loop2_end j print_game_loop2 print_game_loop2_end: li $v0,PRINT_STRING la $a0,disp_bowl_blank_m2 syscall la $a0,endl syscall ################### Bottom part li $v0,PRINT_STRING la $a0,disp_board_pluses syscall la $a0,disp_board_bottom_labels syscall la $a0,disp_player2 syscall jr $ra