From 691664d902192453756c0c8aa471a6d86bf4549e Mon Sep 17 00:00:00 2001 From: JoeStrout Date: Tue, 3 Oct 2023 13:03:47 -0700 Subject: [PATCH] Added MiniScript version of 77_Salvo. --- .../77_Salvo/MiniScript/README.md | 18 ++ .../77_Salvo/MiniScript/salvo.ms | 240 ++++++++++++++++++ 77_Salvo/README.md | 7 +- 3 files changed, 263 insertions(+), 2 deletions(-) create mode 100644 00_Alternate_Languages/77_Salvo/MiniScript/README.md create mode 100644 00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms diff --git a/00_Alternate_Languages/77_Salvo/MiniScript/README.md b/00_Alternate_Languages/77_Salvo/MiniScript/README.md new file mode 100644 index 00000000..d3169446 --- /dev/null +++ b/00_Alternate_Languages/77_Salvo/MiniScript/README.md @@ -0,0 +1,18 @@ +Original source downloaded from [Vintage Basic](http://www.vintage-basic.net/games.html). + +Conversion to [MiniScript](https://miniscript.org). + +Ways to play: + +1. Command-Line MiniScript: +Download for your system from https://miniscript.org/cmdline/, install, and then run the program with a command such as: +``` + miniscript salvo.ms +``` + +2. Mini Micro: +Download Mini Micro from https://miniscript.org/MiniMicro/, launch, and then click the top disk slot and chose "Mount Folder..." Select the folder containing the MiniScript program and this README file. Then, at the Mini Micro command prompt, enter: +``` + load "salvo" + run +``` \ No newline at end of file diff --git a/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms b/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms new file mode 100644 index 00000000..e7c2796e --- /dev/null +++ b/00_Alternate_Languages/77_Salvo/MiniScript/salvo.ms @@ -0,0 +1,240 @@ +print " "*33 + "Salvo" +print " "*15 + "Creative Computing Morristown New Jersey" +print; print; print + +import "listUtil" + +Ship = {} +Ship.name = "Ship" +Ship.positions = null // list of [x,y] coordinates for this ship +Ship.shots = 1 // how many shots this ship provides +Ship.hitPoints = 1 // how many hits this ship can take before sinking +Ship.make = function(name, shots=1, size=2) + result = new Ship + result.name = name + result.shots = shots + result.positions = [] + result.hitPoints = size + return result +end function + +Board = {} +Board.ships = null // list of Ship instances +Board.splash = null // 2D array of: none, or turn on which it was hit +Board.shipAt = function(xy) + for ship in self.ships + if ship.positions.contains(xy) then return ship + end for +end function +Board.isEmptySpot = function(xy) + return 0 < xy[0] < 11 and 0 < xy[1] < 11 and self.shipAt(xy) == null +end function +Board.make = function + result = new Board + result.ships = [] + result.splash = list.init2d(11, 11) + return result +end function +Board.totalShots = function + sum = 0 + for ship in self.ships + sum += ship.shots + end for + return sum +end function + +computerBoard = Board.make +playerBoard = Board.make + +directions = [[-1,-1], [-1,0], [-1,1], [0,-1], [0,1], [1,-1], [1,0], [1,1]] + +randomPosition = function + return [1 + floor(rnd*10), 1 + floor(rnd*10)] +end function + +inputCoords = function(prompt="?") + while true + inp = input(prompt + "? ").replace(",", " ").replace(" ", " ").split + if inp.len != 2 then + print "Please enter coordinates such as: 5,3" + else + x = inp[0].val + y = inp[1].val + if 0 < x < 11 and 0 < y < 11 then return [x,y] + print "X and Y coordinates must be in the range 1-10." + end if + end while +end function + +inBounds = function(pos) + return 0 < pos[0] < 11 and 0 < pos[1] < 11 +end function + +placeComputerShips = function + placeOne = function(ship) + while true + pos = randomPosition + dir = directions.any + ok = true + p = pos[:] + for i in range(0, ship.hitPoints - 1) + if not computerBoard.isEmptySpot(p) then ok = false + p.add dir + end for + if ok then break + end while + for i in range(0, ship.hitPoints - 1) + ship.positions.push pos[:] + pos.add dir + end for + computerBoard.ships.push ship + end function + placeOne Ship.make("Battleship", 3, 5) + placeOne Ship.make("Cruiser", 2, 3) + placeOne Ship.make("Destroyer", 1, 2) + placeOne Ship.make("Destroyer", 1, 2) +end function + +placePlayerShips = function + placeOne = function(ship) + print ship.name + playerBoard.ships.push ship + // Note: like the original BASIC program, we do no validation on + // the input other than making sure it is in range. So you can + // have a ship scattered all over the map, have ships overlap, etc. + for i in range(1, ship.hitPoints) + ship.positions.push inputCoords + end for + end function + print "Enter coordinates for..." + placeOne Ship.make("Battleship", 3, 5) + placeOne Ship.make("Cruiser", 2, 3) + placeOne Ship.make("Destroyer", 1, 2) + placeOne Ship.make("Destroyer", 1, 2) +end function + +printComputerShips = function + for ship in computerBoard.ships + print ship.name + for pos in ship.positions + print " " + pos.join(" ") + end for + end for +end function + +doPlayerTurn = function(turnNum = 1) + shots = playerBoard.totalShots + print "You have " + shots + " shot" + "s"*(shots!=1) + "." + if shots < 1 then + print "I have won." + exit + end if + hits = [] + for i in range(1, shots) + while true + pos = inputCoords + prevHitOnTurn = computerBoard.splash[pos[0]][pos[1]] + if prevHitOnTurn == null then break + print "You shot there before on turn " + prevHitOnTurn + end while + computerBoard.splash[pos[0]][pos[1]] = turnNum + hit = computerBoard.shipAt(pos) + if hit then hits.push hit + end for + for hit in hits + print "You hit my " + hit.name + "." + hit.hitPoints -= 1 + if hit.hitPoints == 0 then + print "...and sank it!" // (not in original BASIC program) + computerBoard.ships.removeVal hit + end if + end for +end function + +pickShot = function + // Pick a spot for the computer to shoot at. We'll do this by + // computing a "neighbor score" for each spot: the number of + // neighboring spots that (1) we have previously hit, and (2) + // contain an enemy ship. Then we'll pick randomly from the + // set of spots with the highest neighbor score. + bestScore = 0 + spots = [] + for i in range(1,10) + for j in range(1,10) + pos = [i,j] + if playerBoard.splash[pos[0]][pos[1]] then continue + score = 0 + for dir in directions + n = pos.plus(dir) + if inBounds(n) and playerBoard.splash[n[0]][n[1]] and playerBoard.shipAt(n) then + score += 1 + end if + end for + if score > bestScore then + bestScore = score + spots = [pos] + else if score == bestScore then + spots.push pos + end if + end for + end for + return spots.any +end function + +doComputerTurn = function(turnNum = 1) + shots = computerBoard.totalShots + print "I have " + shots + " shot" + "s"*(shots!=1) + "." + if shots < 1 then + print "You have won." + exit + end if + hits = [] + for i in range(1, shots) + pos = pickShot + playerBoard.splash[pos[0]][pos[1]] = turnNum + hit = playerBoard.shipAt(pos) + if hit then hits.push hit + if seeComputerShots then print " " + pos.join(" ") + end for + for hit in hits + print "I hit your " + hit.name + "." + hit.hitPoints -= 1 + if hit.hitPoints == 0 then + print "...and sank it!" // (not in original BASIC program) + playerBoard.ships.removeVal hit + end if + end for +end function + +// Main Program + +placeComputerShips +placePlayerShips +while true + yn = input("Do you want to start? ").lower + if yn == "where are your ships?" then + printComputerShips + else if yn and (yn[0] == "y" or yn[0] == "n") then + break + end if +end while +startWithPlayer = (yn[0] == "y") +while true + yn = input("Do you want to see my shots? ").lower + if yn and (yn[0] == "y" or yn[0] == "n") then break +end while +seeComputerShots = (yn[0] == "y") + +turnNumber = 1 +while true + print + print "Turn " + turnNumber + if startWithPlayer then + doPlayerTurn turnNumber + doComputerTurn turnNumber + else + doComputerTurn turnNumber + doPlayerTurn turnNumber + end if + turnNumber += 1 +end while \ No newline at end of file diff --git a/77_Salvo/README.md b/77_Salvo/README.md index 4bb40487..3ef9f21c 100644 --- a/77_Salvo/README.md +++ b/77_Salvo/README.md @@ -26,7 +26,9 @@ http://www.vintage-basic.net/games.html #### Porting Notes -As per the analysis in +The program does no validation of ship positions; your ship coordinates may be scattered around the board in any way you like. (Computer ships will not do this, but they may be placed diagonally in such a way that they cross each other.) Scattering your ships in this way probably defeats whatever all that spaghetti-code logic the computer is using to pick its moves, which is based on the assumption of contiguous ships. + +Moreover: as per the analysis in https://forums.raspberrypi.com/viewtopic.php?p=1997950#p1997950 @@ -40,4 +42,5 @@ This typo is interesting because it causes the program to play by a much weaker `3970 K(R,S)=K(R,S)+E(U)-2*INT(H(U)+.5)` -and to change the JavaScript program accordingly. +and to change the JavaScript program accordingly. (And note that some ports — looking at you, Python — do not implement the original strategy at all, but merely pick random unshot locations for every shot.) +