Post by plus on Jul 24, 2024 14:33:23 GMT
Thanks to tsh73 inspiring version of Battleship I decided to port my version from QB64. It was loaded with sound and image assets for graphics screen but I thought it fun to do a basic b&w version of JB's mainwin screen for the challenge. Yikes! the difference in editors, what a challenge to do so many LOC, ~500, in JB's editor, you do allot of scrolling looking for routines!!!
I hit one of those mysterious bugs when JB gives you not a clue what is wrong. I forgot where the useless error log is, Roaming data somewhere, but I can't find it in Windows Explorer blah! So I just started rebuilding the code adding one routine at a time until I hit the error again, no () allowed in a Sub definition! What a screwy error message you get for that! Anyway figured it out and here is working Battleship game based on Hasbro version.
Some instructions for those unfamiliar with game, Where I say squares I mean character cells:
And the game:
BTW You are in luck! I ported from the dumber AI version I made thinking there would be less code fixes for the JB version.
The smarter version ends a game long before 50% of cells have been bombed, just over 1/3 approximately on average.
b = b + ...
I hit one of those mysterious bugs when JB gives you not a clue what is wrong. I forgot where the useless error log is, Roaming data somewhere, but I can't find it in Windows Explorer blah! So I just started rebuilding the code adding one routine at a time until I hit the error again, no () allowed in a Sub definition! What a screwy error message you get for that! Anyway figured it out and here is working Battleship game based on Hasbro version.
Some instructions for those unfamiliar with game, Where I say squares I mean character cells:
The object of the game is to sink all the Computer's ships before it sinks all yours.
Both the Player and the Computer are given 5 ships to lay out on a 10x10 grid.
The ships are a straight line of squares (2 to 5 squares) forming a long rectangle.
The ships are laid vertically or horizontally on the 10x10 cell grid without overlap.
Each square must be hit by the opponent in order to sink the ship.
The 5 ships are:
Carrier - 5 squares to hit
Battleship - 4 squares to hit
Cruiser - 3 squares to hit
Submarine - 3 squares to hit
Destroyer - 2 squares to hit
The game is started by each opponent laying out their ships secretly to the other.
You the Player must setup your ships on the left board.
They are setup in same order I listed above.
If you do not want the computer to setup for you:
So the first ship to set up will be the Carrier that is 5 squares long.
Enter v or h for horizontal or vertical then the column letter don't worry about capitals then
the digit 0 to 9. So that is 3 chraracters the first v or h, the 2nd abcdefghi or j and 3rd
is 0 to 9. Be careful not to click other places on board because printing will start there
and mess up board.
If there is room on board to lay out all 5 across AND this ship does not overlap another,
then the rest of the ship will be drawn in with its numbers 1 - 5 for the player. The computers
ship numbers are 6-10 but 10 is shown as 0 (after you lose and the comnputers ship placement is
displayed, you can see these before next game is started.)
(Of course, the first ship can't overlap another but every other ship has that potential.)
If there is not room or the ship would overlap another,
then you must start over with the prompt to lay the ship horizontally or vertically...
When you get all 5 of your ships laid out on the 10x10 grid on the left, the shooting match begins!
You will be prompted to Enter a cell on the right 10x10 board to guess where a Computer's ship might be.
If you hit a cell of the Computers ships an X for hit appears at that cell.
If you miss all the Computers ships, an O will appear = miss
The Computer will then take a shot and your board will show an X or O according to the Computer's hit or miss.
Then it's your turn again. If you had a hit the last turn you will likely want to find the
rest of the ship to sink it. So Enter cells above, below, left or right of the hit.
A 2nd hit will tell you if the ship is laid out horizontally or vertically.
A 2nd hit would actually sink a Destroyer because it is only 2 squares long.
So you scout around the 10x10 board making random shots (or systematically cover the board with shots)
until you find a ship, sink it and go hunting for the next ship to sink until you get all 5.
Meanwhile the Computer is doing the same thing, so whichever opponent sinks all the ships first, wins!
Oh a caveat!
It is possible to align the ships side by side or one end up next to another ship
(as long as they don't overlap). This makes it confusing as you might be hitting 2 different ships
with your shots, so pay close attention to which ship is announced sunk, you might have more hits
in the same area than how many it took to sink the ship.
en.wikipedia.org/wiki/Battleship_game
Both the Player and the Computer are given 5 ships to lay out on a 10x10 grid.
The ships are a straight line of squares (2 to 5 squares) forming a long rectangle.
The ships are laid vertically or horizontally on the 10x10 cell grid without overlap.
Each square must be hit by the opponent in order to sink the ship.
The 5 ships are:
Carrier - 5 squares to hit
Battleship - 4 squares to hit
Cruiser - 3 squares to hit
Submarine - 3 squares to hit
Destroyer - 2 squares to hit
The game is started by each opponent laying out their ships secretly to the other.
You the Player must setup your ships on the left board.
They are setup in same order I listed above.
If you do not want the computer to setup for you:
So the first ship to set up will be the Carrier that is 5 squares long.
Enter v or h for horizontal or vertical then the column letter don't worry about capitals then
the digit 0 to 9. So that is 3 chraracters the first v or h, the 2nd abcdefghi or j and 3rd
is 0 to 9. Be careful not to click other places on board because printing will start there
and mess up board.
If there is room on board to lay out all 5 across AND this ship does not overlap another,
then the rest of the ship will be drawn in with its numbers 1 - 5 for the player. The computers
ship numbers are 6-10 but 10 is shown as 0 (after you lose and the comnputers ship placement is
displayed, you can see these before next game is started.)
(Of course, the first ship can't overlap another but every other ship has that potential.)
If there is not room or the ship would overlap another,
then you must start over with the prompt to lay the ship horizontally or vertically...
When you get all 5 of your ships laid out on the 10x10 grid on the left, the shooting match begins!
You will be prompted to Enter a cell on the right 10x10 board to guess where a Computer's ship might be.
If you hit a cell of the Computers ships an X for hit appears at that cell.
If you miss all the Computers ships, an O will appear = miss
The Computer will then take a shot and your board will show an X or O according to the Computer's hit or miss.
Then it's your turn again. If you had a hit the last turn you will likely want to find the
rest of the ship to sink it. So Enter cells above, below, left or right of the hit.
A 2nd hit will tell you if the ship is laid out horizontally or vertically.
A 2nd hit would actually sink a Destroyer because it is only 2 squares long.
So you scout around the 10x10 board making random shots (or systematically cover the board with shots)
until you find a ship, sink it and go hunting for the next ship to sink until you get all 5.
Meanwhile the Computer is doing the same thing, so whichever opponent sinks all the ships first, wins!
Oh a caveat!
It is possible to align the ships side by side or one end up next to another ship
(as long as they don't overlap). This makes it confusing as you might be hitting 2 different ships
with your shots, so pay close attention to which ship is announced sunk, you might have more hits
in the same area than how many it took to sink the ship.
en.wikipedia.org/wiki/Battleship_game
And the game:
' Battleship based on Hasbro Game b+ 2024-07-23 allot of code ported out of old QB64 version
Global PXO, PYO, CXO, CYO, SXO, SYO ' see setup where these are set with Game Board
Global dir, rI, rMax, currentHits, pTurn, GameOn ' preserve between AI calls
Global x1, y1, bombx, bomby, hit2
while 1
scan
call Setup
call Shoot
wend
sub Setup
' clear arrays and variables
dim shipX(10), shipY(10), shipLen(10), shipName$(10), shipHor(10), shipHits$(10), shipSunk(10) ' not using 0 index
dim p(9, 9), c(9, 9)
' for Computer AI
dim hits(9, 9), rX(100), rY(100)
dir = 0: rI = 0: rMax = 0: currentHits = 0: pTurn = 0: GameOn = 0 ' globals
'setup random bombing locations
For y = 0 To 9
If y Mod 2 = 0 Then startX = 1 Else startX = 0
For x = startX To 9 Step 2
rX(rI) = x: rY(rI) = y: rI = rI + 1
Next
Next
'now shuffle the coverage for random luck at finding first hits
rMax = rI - 1
For i = rMax To 1 Step -1
r = Int(Rnd(0) * (i + 1))
t = rX(i) : rX(i) = rX(r) : rX(r) = t
t = rY(i) : rY(i) = rY(r) : rY(r) = t
Next
rI = 0
cls
' Game Board draw once per game
Print ""
Print " Player Computer"
Print ""
Print " A B C D E F G H I J A B C D E F G H I J"
Print " ------------------- -------------------"
Print " 0| . . . . . . . . . . 0| . . . . . . . . . ."
Print " 1| . . . . . . . . . . 1| . . . . . . . . . ."
Print " 2| . . . . . . . . . . 2| . . . . . . . . . ."
Print " 3| . . . . . . . . . . 3| . . . . . . . . . . Ships: P C"
Print " 4| . . . . . . . . . . 4| . . . . . . . . . . Carrier . ."
Print " 5| . . . . . . . . . . 5| . . . . . . . . . . Battleship . ."
Print " 6| . . . . . . . . . . 6| . . . . . . . . . . Cruiser . ."
Print " 7| . . . . . . . . . . 7| . . . . . . . . . . Submarine . ."
Print " 8| . . . . . . . . . . 8| . . . . . . . . . . Destroyer . ."
Print " 9| . . . . . . . . . . 9| . . . . . . . . . ."
Print " ------------------- -------------------"
Print " A B C D E F G H I J A B C D E F G H I J"
'locate 5, 6: print "X" ' check offsets
PXO = 5 : PYO = 6 ' offsets for player grid
CXO = 32 : CYO = 6 ' offsets for computer grid player shoots at
SXO = 65 : SYO = 10 ' offsets ship tally
For i = 1 To 10 ' not sure I need names yet
Select Case i
Case 1: shipLen(i) = 5: shipName$(i) = "Carrier"
Case 2: shipLen(i) = 4: shipName$(i) = "Battleship"
Case 3: shipLen(i) = 3: shipName$(i) = "Cruiser"
Case 4: shipLen(i) = 3: shipName$(i) = "Submarine"
Case 5: shipLen(i) = 2: shipName$(i) = "Destroyer"
Case 6: shipLen(i) = 5: shipName$(i) = "Carrier"
Case 7: shipLen(i) = 4: shipName$(i) = "Battleship"
Case 8: shipLen(i) = 3: shipName$(i) = "Cruiser"
Case 9: shipLen(i) = 3: shipName$(i) = "Submarine"
Case 10: shipLen(i) = 2: shipName$(i) = "Destroyer"
End Select
Next
For i = 1 To 10
shipHits$(i) = String$(shipLen(i), "o")
Next
call autosetup 1
call message "Let computer setup your ships? press y for yes, n for no..."
k$ = Input$(1)
If k$ = "y" Then
call autosetup 0
Else
For S = 1 To 5
OK = 0
locate 1, 21
print "To place ship:"+chr$(13)+"Enter v for vertical, h for horizontal, letter and digit for top, left of ship"
While OK = 0
call clearMessage
call message "Setting up the ";shipName$(S);" with length ";shipLen(S)
Locate 1, 23: Print Space$(80);
locate 1, 23: input "placement? "; place$
place$ = upper$(place$)
If Left$(place$, 1) = "V" Then shipHor(S) = 0 Else shipHor(S) = 1
sx = InStr("ABCDEFGHIJ", Mid$(place$, 2, 1)) -1
sy = val(mid$(place$, 3, 1))
locate 1, 23: print space$(80);
If shipHor(S) Then
If sx <= 10 - shipLen(S) Then
OK = 1
For xx = 0 To shipLen(S) - 1
If p(sx + xx, sy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For xx = 0 To shipLen(S) - 1
p(sx + xx, sy) = S
call LP sx + xx, sy, "p", str$(S)
Next
End If
End If
Else
If sy <= 10 - shipLen(S) Then
OK = 1
For yy = 0 To shipLen(S) - 1
If p(sx, sy + yy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For yy = 0 To shipLen(S) - 1
p(sx, sy + yy) = S
call LP sx, sy + yy, "p", str$(S)
Next
End If
End If
End If
Wend
Next
locate 1, 21: print space$(80);
locate 1, 22: print space$(80);
locate 1, 23: print space$(80);
End If
call clearMessage
end sub
Sub autosetup AItf ' there is surely a shorter way to do this but I am eager to get on with other stuff
If AItf Then 'setup Computer's ships
'setup a board with ships, Computer or AI's setup
For S = 6 To 10
OK = 0
While OK = 0
shipHor(S) = rand(0, 1)
If shipHor(S) Then
sy = rand(0, 9)
sx = rand(0, 10 - shipLen(S))
OK = 1
For xx = 0 To shipLen(S) - 1
If c(sx + xx, sy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For xx = 0 To shipLen(S) - 1
c(sx + xx, sy) = S
'call LP sx + xx, sy, "c", str$(S mod 10) ' for debugg
Next
End If
Else
sx = rand(0, 9)
sy = rand(0, 10 - shipLen(S))
OK = 1
For yy = 0 To shipLen(S) - 1
If c(sx, sy + yy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For yy = 0 To shipLen(S) - 1
c(sx, sy + yy) = S
'call LP sx, sy + yy, "c", str$(S mod 10) ' for debugg
Next
End If
End If
Wend
Next
Else 'setup Player's ships
For S = 1 to 5
OK = 0
While OK = 0
shipHor(S) = rand(0, 1)
If shipHor(S) Then
sy = rand(0, 9)
sx = rand(0, 10 - shipLen(S))
OK = 1
For xx = 0 To shipLen(S) - 1
If p(sx + xx, sy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For xx = 0 To shipLen(S) - 1
p(sx + xx, sy) = S
call LP sx + xx, sy, "p", str$(S)
Next
End If
Else
sx = rand(0, 9)
sy = rand(0, 10 - shipLen(S))
OK = 1
For yy = 0 To shipLen(S) - 1
If p(sx, sy + yy) <> 0 Then OK = 0: Exit For
Next
If OK Then
shipX(S) = sx: shipY(S) = sy
For yy = 0 To shipLen(S) - 1
p(sx, sy + yy) = S
call LP sx, sy + yy, "p", str$(S)
Next
End If
End If
Wend
Next
End If
End Sub
sub message m$
locate 1, 20: print m$;
end sub
sub clearMessage
locate 1, 20: print space$(80);
locate 1, 20: print ""
end sub
sub LP x, y, pcGrid$, s$
if pcGrid$ = "p" then locate PXO + 2 * x, PYO + y else locate CXO + 2 * x, CYO + y
Print s$;
end sub
function rand(low, high) ' Random integer from low to high inclusive
rand = int(rnd(0) * (high - low + 1)) + low
end function
sub Shoot
GameOn = 1
While GameOn
scan
'call updateStatus ' really needed here?
If pTurn Then pTurn = 0 Else pTurn = 1
If pTurn Then
Locate 1, 20: Input "Enter your next bomb site letter digit "; place$
If place$ = "" Then GameOn = 0
place$ = upper$(place$)
bx = instr("ABCDEFGHIJ", left$(place$, 1)) - 1
by = val(mid$(place$, 2, 1))
if bx >= 0 and bx < 10 then ' better check
if by >= 0 and by < 10 then
If c(bx, by) <> 0 Then 'hit
call LP bx, by, "c", "X"
call hitEval "c", bx, by 'game could end here
Else
call LP bx, by, "c", "o"
End If
end if
end if
call clearMessage
Else
'AI's turn if it gets a hit it will bomb around the ship until it is finished
'could be trouble if 2 ships are next to each other, damn until just now I hadn't anticipated this
'hits board tracks red and white pegs like a human player for AI
While hits(cx(checkIndex), cy(checkIndex)) <> 0
checkIndex = checkIndex - 1
If checkIndex <= 0 Then checkIndex = 0: Exit While
Wend
If dir Then 'we have a bomb location all set to test
If p(bombx, bomby) <> 0 Then 'hit!
hit2 = 1
hits(bombx, bomby) = 1: currentHits = currentHits + 1
call LP bombx, bomby, "p", "X"
'we need to know stuff but can't use this info for AI finding the ship
'when hitEval announces a ship sunk we can reduce the currentHits count by that ships amount
'if still have more current hits, continue bombing area as another ship is there
call hitEval "p", bombx, bomby 'this will reduce currentHits by the amount a ship could take when sunk
If currentHits = 0 Then 'clear our checklist we sank all ships we hit, call off bombing of area
x1 = 0: y1 = 0: dir = 0
Else
call decideWhereToBombNext
End If
Else 'no hit from checklist scratch off one item
hit2 = 0
hits(bombx, bomby) = -1
call LP bombx, bomby, "p", "o"
call decideWhereToBombNext
End If ' are we still working on hit
Else
'not working on any hits x1, y1 = 0, dir = 0, currentHits might be = 0
'random but systematic shooting, bring up next good shooting location
While hits(rX(rI), rY(rI)) <> 0
rI = rI + 1
'if rI exceeds rMax then hits is loaded
Wend
'consider that shot just fired was it a hit or miss
If p(rX(rI), rY(rI)) <> 0 Then ' test our shot just fired is hit!
x1 = rX(rI): y1 = rY(rI) 'save first hit to come back to
hits(x1, y1) = 1: currentHits = currentHits + 1
call LP x1, y1, "p", "X"
'we need to know stuff but can't use this info for AI finding the ship
'it's the same as for the player
call hitEval "p", x1, y1
'did we just happen to finish off a ship? current hits = 0
If currentHits = 0 Then 'must of finished off an ship
x1 = 0: y1 = 0: dir = 0 'we are done
Else
dir = -1
call decideWhereToBombNext
End If
Else 'no hit
hits(rX(rI), rY(rI)) = -1
call LP rX(rI), rY(rI), "p", "o"
End If
rI = rI + 1 'we used rI so get next bomb index ready
If rI > rMax Then 'reload with next set of coodinates
rI = 0
For y = 0 To sqPerSide - 1
If y Mod 2 = 0 Then startX = 0 Else startX = 1
For x = startX To sqPerSide - 1 Step 2
rX(rI) = x: rY(rI) = y: rI = rI + 1
Next
Next
'now shuffle the coverage for random luck at finding first hits
rMax = rI - 1
For i = rMax To 1 Step -1
r = Int(Rnd(0) * (i + 1))
t = rX(i) : rX(i) = rX(r) : rX(r) = t
t = rY(i) : rY(i) = rY(r) : rY(r) = t
Next
rI = 0
End If 'getting rI ready
End If 'rI was hit or not
End If 'whose turn is it
Wend
call message "Play Again? press y for yes, n for no..."
k$ = input$(1)
cls
if k$ = "n" then end
end sub
Sub hitEval board$, bbx, bby
'this is like a referee / judge for both players to announce a ship sunk and a game won?
'notice "hitEval"+chr$(13)+board$+", bx,by" + str$(bbx) +", "+str$(bby)
If board$ <> "p" Then
S = c(bbx, bby) ' which ship number
you$ = "Player"
my$ = "Computer's"
istart = 6
istop = 10
Else
S = p(bbx, bby)
you$ = "Computer"
my$ = "Player's"
istart = 1
istop = 5
End If
If shipHor(S) Then d = bbx - shipX(S) + 1 Else d = bby - shipY(S) + 1
'notice "s d = "+chr$(13) + str$(S) + ", "+str$(d)
shipHits$(S) = SubStr$(shipHits$(S), d, "X")
'notice "shipHits$ S"+Chr$(13)+shipHits$(S) + " "+str$(S)
If shipHits$(S) = String$(shipLen(S), "X") Then
shipSunk(S) = 1
If board$ = "p" Then currentHits = currentHits - shipLen(S)
call updateStatus
notice you$ + " sank " + my$ + " " + shipName$(S); "!"
tot = 0
For i = istart To istop
If shipSunk(i) = 1 Then tot = tot + 1
Next
If tot = 5 Then
call updateStatus
if you$ = "Computer" then call ShowComputersShips
Notice "Congratulations " + you$ + ", you sank all " + my$ + " ships! GameOver..."
GameOn = 0
End If
End If
End Sub
Sub decideWhereToBombNext
'find next good location, mark the direction we took
If dir = -1 Then ' we just got a fresh hit the rest of the ship is in 1 of 4 directions
hit2 = 0 'when direction = 0 reset 2nd hit signal to 0
If x1 + 1 <= 9 Then
If hits(x1 + 1, y1) = 0 Then
bombx = x1 + 1: bomby = y1: dir = 1: Exit Sub
End If
End If
'still here?
If y1 + 1 <= 9 - 1 Then
If hits(x1, y1 + 1) = 0 Then
bombx = x1: bomby = y1 + 1: dir = 2: Exit Sub
End If
End If
'still here?
If x1 - 1 >= 0 Then
If hits(x1 - 1, y1) = 0 Then
bombx = x1 - 1: bomby = y1: dir = 3: Exit Sub
End If
End If
'still here OK this has to do it!
If y1 - 1 >= 0 Then
If hits(x1, y1 - 1) = 0 Then
bombx = x1: bomby = y1 - 1: dir = 4: Exit Sub
End If
End If
'still here ???? damn! give up and go back to random shots
dir = 0: Exit Sub ' < this signals that
End If
'setup next bombx, bomby
If hit2 Then 'whatever direction we are taking, continue if we can
Select Case dir
Case 1
If bombx + 1 <= 9 Then
If hits(bombx + 1, bomby) = 0 Then
bombx = bombx + 1: Exit Sub
End If
End If
Case 2
If bomby + 1 <= 9 Then
If hits(bombx, bomby + 1) = 0 Then
bomby = bomby + 1: Exit Sub
End If
End If
Case 3
If bombx - 1 >= 0 Then
If hits(bombx - 1, bomby) = 0 Then
bombx = bombx - 1: Exit Sub
End If
End If
Case 4
If bomby - 1 >= 0 Then
If hits(bombx, bomby - 1) = 0 Then
bomby = bomby - 1: dir = 4: Exit Sub
End If
End If
End Select
End If
'still here? then we have to change direction and go back to x1, y1 the first hit
hit2 = 0 'reset this for the new direction check
While dir < 4
dir = dir + 1
Select Case dir
Case 2
If y1 + 1 <= 9 Then
If hits(x1, y1 + 1) = 0 Then
bombx = x1: bomby = y1 + 1: Exit Sub
End If
End If
Case 3
If x1 - 1 >= 0 Then
If hits(x1 - 1, y1) = 0 Then
bombx = x1 - 1: bomby = y1: Exit Sub
End If
End If
Case 4
If y1 - 1 >= 0 Then
If hits(x1, y1 - 1) = 0 Then
bombx = x1: bomby = y1 - 1: Exit Sub
End If
End If
End Select
Wend
'still here, well we've run out of directions
dir = 0 'back to random bombing
End Sub
function SubStr$(s$, place, subst$)
for i = 1 to len(s$)
if i = place then r$ = r$ + subst$ else r$ = r$ + mid$(s$, i, 1)
next
SubStr$ = r$
end function
function String$(number, s$)
for i = 1 to number
r$ = r$ + s$
next
String$ = r$
end function
Sub updateStatus ' ships area
For i = 1 To 5 ' row 10 carrier player colum 66 computer column 68
If shipSunk(i) Then Locate 65, i + 9: Print "X";
If shipSunk(i + 5) Then Locate 67, i + 9: Print "X"
Next
End Sub
sub ShowComputersShips
For S = 6 To 10
If shipHor(S) Then
sx = shipX(S): sy = shipY(S)
For xx = 0 To shipLen(S) - 1
call LP sx + xx, sy, "c", str$(S mod 10) ' for debugg
Next
Else
sx = shipX(S) : sy = shipY(S)
For yy = 0 To shipLen(S) - 1
call LP sx, sy + yy, "c", str$(S mod 10) ' for debugg
Next
End If
Next
end sub
BTW You are in luck! I ported from the dumber AI version I made thinking there would be less code fixes for the JB version.
The smarter version ends a game long before 50% of cells have been bombed, just over 1/3 approximately on average.
b = b + ...