diff --git a/config.json b/config.json index b8f773d..56e9d44 100644 --- a/config.json +++ b/config.json @@ -505,6 +505,14 @@ "loops", "math" ] + }, + { + "slug": "minesweeper", + "name": "Minesweeper", + "uuid": "3c65a63d-f788-47a1-9615-8cab7e7974dd", + "practices": [], + "prerequisites": [], + "difficulty": 8 } ] }, diff --git a/exercises/practice/minesweeper/.docs/hints.md b/exercises/practice/minesweeper/.docs/hints.md new file mode 100644 index 0000000..52d65b0 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/hints.md @@ -0,0 +1,6 @@ +# Hints + +## General + +- The `$t0-9` registers can be used to temporarily store values +- The instructions specify which registers are used as input and output diff --git a/exercises/practice/minesweeper/.docs/instructions.append.md b/exercises/practice/minesweeper/.docs/instructions.append.md new file mode 100644 index 0000000..30367e3 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/instructions.append.md @@ -0,0 +1,15 @@ +# Instructions append + +## Minefield format + +The minefield is represented as a null-terminated string, with a newline character at the end of each row. + +An example would be `" \n * \n \n"` + +## Registers + +| Register | Usage | Type | Description | +| -------- | ------------ | ------- | ----------------------------- | +| `$a0` | input | address | null-terminated input string | +| `$a1` | input/output | address | null-terminated output string | +| `$t0-9` | temporary | any | for temporary storage | diff --git a/exercises/practice/minesweeper/.docs/instructions.md b/exercises/practice/minesweeper/.docs/instructions.md new file mode 100644 index 0000000..7c1df2e --- /dev/null +++ b/exercises/practice/minesweeper/.docs/instructions.md @@ -0,0 +1,26 @@ +# Instructions + +Your task is to add the mine counts to empty squares in a completed Minesweeper board. +The board itself is a rectangle composed of squares that are either empty (`' '`) or a mine (`'*'`). + +For each empty square, count the number of mines adjacent to it (horizontally, vertically, diagonally). +If the empty square has no adjacent mines, leave it empty. +Otherwise replace it with the adjacent mines count. + +For example, you may receive a 5 x 4 board like this (empty spaces are represented here with the '·' character for display on screen): + +```text +·*·*· +··*·· +··*·· +····· +``` + +Which your code should transform into this: + +```text +1*3*1 +13*31 +·2*2· +·111· +``` diff --git a/exercises/practice/minesweeper/.docs/introduction.md b/exercises/practice/minesweeper/.docs/introduction.md new file mode 100644 index 0000000..5f74a74 --- /dev/null +++ b/exercises/practice/minesweeper/.docs/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +[Minesweeper][wikipedia] is a popular game where the user has to find the mines using numeric hints that indicate how many mines are directly adjacent (horizontally, vertically, diagonally) to a square. + +[wikipedia]: https://en.wikipedia.org/wiki/Minesweeper_(video_game) diff --git a/exercises/practice/minesweeper/.meta/config.json b/exercises/practice/minesweeper/.meta/config.json new file mode 100644 index 0000000..2822097 --- /dev/null +++ b/exercises/practice/minesweeper/.meta/config.json @@ -0,0 +1,17 @@ +{ + "authors": [ + "keiravillekode" + ], + "files": { + "solution": [ + "impl.mips" + ], + "test": [ + "runner.mips" + ], + "example": [ + ".meta/example.mips" + ] + }, + "blurb": "Add the numbers to a minesweeper board." +} diff --git a/exercises/practice/minesweeper/.meta/example.mips b/exercises/practice/minesweeper/.meta/example.mips new file mode 100644 index 0000000..821f36a --- /dev/null +++ b/exercises/practice/minesweeper/.meta/example.mips @@ -0,0 +1,110 @@ +# | Register | Usage | Type | Description | +# | -------- | ------------ | ------- | --------------------------------------------- | +# | `$a0` | input | address | null-terminated input string | +# | `$a1` | input/output | address | null-terminated output string | +# | `$t0` | temporary | address | pointer into input | +# | `$t1` | temporary | byte | input character | +# | `$t2` | temporary | address | line length (including newline character) | +# | `$t3` | temporary | address | location of input's null terminator | +# | `$t4` | temporary | address | previous row (or current for first row) | +# | `$t5` | temporary | address | current row | +# | `$t6` | temporary | address | next row (or current for last row) | +# | `$t7` | temporary | integer | previous column (or current for first column) | +# | `$t8` | temporary | integer | current column | +# | `$t9` | temporary | integer | next column (or current for last column) | +# | `$a2` | temporary | address | row of adjacent square | +# | `$a3` | temporary | integer | column of adjacent square | +# | `$v0` | temporary | byte | newline character | +# | `$v1` | temporary | integer | number of adjacent mines | +# | `$s0` | temporary | byte | mine character '*' | + +.globl annotate + +annotate: + move $t0, $a0 + lb $t1, 0($t0) # read first byte of minefield + beqz $t1, return + + subi $sp, $sp, 4 # preserve original $s0 value on stack + sw $s0, 0($sp) + li $s0, '*' # mine character + li $v0, '\n' + +find_newline: + lb $t1, 0($t0) + addi $t0, $t0, 1 + bne $t1, $v0, find_newline + sub $t2, $t0, $a0 # line length (including newline character) + move $t0, $a0 + +find_null: + add $t0, $t0, $t2 # jump ahead by line length + lb $t1, 0($t0) + bnez $t1, find_null + move $t3, $t0 # location of input's null terminator + + move $t5, $a0 + move $t6, $a0 # start of first row + +next_row: + move $t8, $zero # first column + move $t9, $zero + move $t4, $t5 # current row becomes previous row + move $t5, $t6 # next row becomes current row + + add $t6, $t5, $t2 # next row + bne $t6, $t3, first_column + move $t6, $t5 # last row + +first_column: + beq $t2, 1, write_newline # jump ahead if rows contain no squares + +next_column: + move $t7, $t8 # current column becomes previous column + move $t8, $t9 # next column becomes current column + addi $t9, $t8, 2 + sne $t9, $t9, $t2 # Set $t9 to 1 if there are more columns, otherwise 0 + add $t9, $t8, $t9 # next column + + add $t0, $t5, $t8 # address of minefield square + lb $t1, 0($t0) + beq $t1, $s0, write_square # jump ahead if we have reached a mine + + move $v1, $zero # number of adjacent mines + subu $a2, $t4, $t2 + +adjacent_row: + addu $a2, $a2, $t2 # address of adjacent row: $t4, $t4+$t2, $t6 + subiu $a3, $t7, 1 + +adjacent_column: + addiu $a3, $a3, 1 # index of adjacent column: $t7, $t7+1, $t9 + + add $t0, $a2, $a3 # address of adjacent square + lb $t1, 0($t0) + seq $t0, $t1, $s0 # 1 if square contains mine, 0 otherwise + add $v1, $v1, $t0 # update mine count + + bne $a3, $t9, adjacent_column + bne $a2, $t6, adjacent_row + + li $t1, ' ' + beqz $v1, write_square + addi $t1, $v1, '0' # mine count, as ASCII digit + +write_square: + sb $t1, 0($a1) + addi $a1, $a1, 1 # increment output pointer + bne $t8, $t9, next_column + +write_newline: + sb $v0, 0($a1) # write '\n' + addi $a1, $a1, 1 + bne $t5, $t6, next_row + + lw $s0, 0($sp) # restore original $s0 value + addi $sp, $sp, 4 + +return: + sb $zero, 0($a1) # null terminator + jr $ra diff --git a/exercises/practice/minesweeper/.meta/tests.toml b/exercises/practice/minesweeper/.meta/tests.toml new file mode 100644 index 0000000..2a14222 --- /dev/null +++ b/exercises/practice/minesweeper/.meta/tests.toml @@ -0,0 +1,46 @@ +# This is an auto-generated file. +# +# Regenerating this file via `configlet sync` will: +# - Recreate every `description` key/value pair +# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications +# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion) +# - Preserve any other key/value pair +# +# As user-added comments (using the # character) will be removed when this file +# is regenerated, comments can be added via a `comment` key. + +[0c5ec4bd-dea7-4138-8651-1203e1cb9f44] +description = "no rows" + +[650ac4c0-ad6b-4b41-acde-e4ea5852c3b8] +description = "no columns" + +[6fbf8f6d-a03b-42c9-9a58-b489e9235478] +description = "no mines" + +[61aff1c4-fb31-4078-acad-cd5f1e635655] +description = "minefield with only mines" + +[84167147-c504-4896-85d7-246b01dea7c5] +description = "mine surrounded by spaces" + +[cb878f35-43e3-4c9d-93d9-139012cccc4a] +description = "space surrounded by mines" + +[7037f483-ddb4-4b35-b005-0d0f4ef4606f] +description = "horizontal line" + +[e359820f-bb8b-4eda-8762-47b64dba30a6] +description = "horizontal line, mines at edges" + +[c5198b50-804f-47e9-ae02-c3b42f7ce3ab] +description = "vertical line" + +[0c79a64d-703d-4660-9e90-5adfa5408939] +description = "vertical line, mines at edges" + +[4b098563-b7f3-401c-97c6-79dd1b708f34] +description = "cross" + +[04a260f1-b40a-4e89-839e-8dd8525abe0e] +description = "large minefield" diff --git a/exercises/practice/minesweeper/impl.mips b/exercises/practice/minesweeper/impl.mips new file mode 100644 index 0000000..48c6bf6 --- /dev/null +++ b/exercises/practice/minesweeper/impl.mips @@ -0,0 +1,10 @@ +# | Register | Usage | Type | Description | +# | -------- | ------------ | ------- | ----------------------------- | +# | `$a0` | input | address | null-terminated input string | +# | `$a1` | input/output | address | null-terminated output string | +# | `$t0-9` | temporary | any | for temporary storage | + +.globl annotate + +annotate: + jr $ra diff --git a/exercises/practice/minesweeper/runner.mips b/exercises/practice/minesweeper/runner.mips new file mode 100644 index 0000000..e7fca33 --- /dev/null +++ b/exercises/practice/minesweeper/runner.mips @@ -0,0 +1,213 @@ +# +# Test annotate with some examples +# +# a0 - input string, for callee +# a1 - pointer to output string, for callee +# s0 - num of tests left to run +# s1 - address of input string +# s2 - address of expected output string +# s3 - char byte of input +# s4 - char byte of output +# s5 - copy of output location + +.eqv BUFFER_SIZE 44 + +.data + +# number of test cases +n: .word 12 +# input values +ins: .ascii + "\0", + + "\n\0", + + " \n", + " \n", + " \n\0", + + "***\n", + "***\n", + "***\n\0", + + " \n", + " * \n", + " \n\0", + + "***\n", + "* *\n", + "***\n\0", + + " * * \n\0", + + "* *\n\0", + + " \n", + "*\n", + " \n", + "*\n", + " \n\0", + + "*\n", + " \n", + " \n", + " \n", + "*\n\0", + + " * \n", + " * \n", + "*****\n", + " * \n", + " * \n\0", + + " * * \n", + " * \n", + " * \n", + " * *\n", + " * * \n", + " \n\0" +# expected output values +outs: .ascii + "\0", + + "\n\0", + + " \n", + " \n", + " \n\0", + + "***\n", + "***\n", + "***\n\0", + + "111\n", + "1*1\n", + "111\n\0", + + "***\n", + "*8*\n", + "***\n\0", + + "1*2*1\n\0", + + "*1 1*\n\0", + + "1\n", + "*\n", + "2\n", + "*\n", + "1\n\0", + + "*\n", + "1\n", + " \n", + "1\n", + "*\n\0", + + " 2*2 \n", + "25*52\n", + "*****\n", + "25*52\n", + " 2*2 \n\0", + + "1*22*1\n", + "12*322\n", + " 123*2\n", + "112*4*\n", + "1*22*2\n", + "111111\n\0" + +failmsg: .asciiz "failed for test input:\n" +expectedmsg: .asciiz "expected\n" +tobemsg: .asciiz "to be\n" +okmsg: .asciiz "all tests passed" + + +.text + +runner: + lw $s0, n + la $s1, ins + la $s2, outs + + li $v0, 9 # code for allocating heap memory + li $a0, BUFFER_SIZE # specify length of longest expected output + syscall + move $s5, $v0 # location of allocated memory is where callee writes result + +run_test: + jal clear_output # zero out output location + move $a0, $s1 # load input value into a0 + move $a1, $s5 # load destination address into a1 + jal annotate # call subroutine under test + move $a1, $s5 + move $s6, $s5 # take copy of output value + move $s7, $s2 + +scan: + lb $s3, 0($s2) # load one byte of the expectation + lb $s4, 0($a1) # load one byte of the actual + bne $s3, $s4, exit_fail # if the two differ, the test has failed + addi $s2, $s2, 1 # point to next expectation byte + addi $a1, $a1, 1 # point to next actual byte + bne $s3, $zero, scan # if one char (and therefore the other) was not null, loop + +input_scan: + lb $s3, 0($s1) + addi $s1, $s1, 1 + bne $s3, $zero, input_scan + +done_scan: + sub $s0, $s0, 1 # decrement num of tests left to run + bgt $s0, $zero, run_test # if more than zero tests to run, jump to run_test + +exit_ok: + la $a0, okmsg # put address of okmsg into a0 + li $v0, 4 # 4 is print string + syscall + + li $v0, 10 # 10 is exit with zero status (clean exit) + syscall + +exit_fail: + la $a0, failmsg # put address of failmsg into a0 + li $v0, 4 # 4 is print string + syscall + + move $a0, $s1 # print input that failed on + li $v0, 4 + syscall + + la $a0, expectedmsg + li $v0, 4 + syscall + + move $a0, $s6 # print actual that failed on + li $v0, 4 + syscall + + la $a0, tobemsg + li $v0, 4 + syscall + + move $a0, $s7 # print expected value that failed on + li $v0, 4 + syscall + + li $a0, 1 # set error code to 1 + li $v0, 17 # 17 is exit with error + syscall + + +clear_output: + # zero out output by storing zeros + addi $t0, $s5, BUFFER_SIZE # pointer to end of output buffer + +clear: + subi $t0, $t0, 4 # decrement pointer + sw $zero, 0($t0) # store a 0 word + bne $t0, $s5, clear # repeat util we have reached the start of the buffer + jr $ra + +# # Include your implementation here if you wish to run this from the MARS GUI. +# .include "impl.mips"