12  Monte Carlo Battleship

Author

Brian Powers

Published

September 26, 2024

This code will demonstrate how you can use Monte Carlo Simulation to create an AI opponent in Battleship.

First a few function that will be used throughout.

# constants representing hits and misses.
HIT <- 9; MISS <- -9

placeShip <- function(board, shipIndex){
  isLegal <- FALSE
  giveUp <- 0
  while(!isLegal & giveUp < 100){
    giveUp <- giveUp+1
    orientation = sample(2,1) #upright or across
    if(orientation==2) {board <- t(board)}
    shipLength = shipSizes[shipIndex]
    shipX = sample(1:(dim(board)[1]-shipLength+1),1)
    shipY = sample(1:(dim(board)[2]),1)
    isLegal = prod(board[shipX:(shipX+shipLength-1), shipY] %in% c(0,HIT))
    if(isLegal){
      board[shipX:(shipX+shipLength-1), shipY] <- shipIndex
    }
    if(orientation==2) {board <- t(board)}
  }
  if(isLegal){
    return(board)
  } else{
    return(FALSE)
  }
}

placeAllShips <- function(board){
  for(i in which(shipAlive)){
    board <- placeShip(board,i)
    if(!is.matrix(board)) return(FALSE)
  }
  if(sum(board==HIT)==0){
    return(board)
  } else {
    return(FALSE)
  }
}

buildHeatMap <- function(boardKnowledge, NMC=10000){
  boardMap <- matrix(data=0, nrow=boardDim[1], ncol=boardDim[2])
  for(i in 1:NMC){
    placeAll <- placeAllShips(boardKnowledge) 
    if(is.matrix(placeAll)){
      boardMap <- boardMap + (placeAll %in% (1:length(shipAlive))[shipAlive])
    }
  }
  return(boardMap)
}

guessACoord <- function(boardKnowledge, NMC=10000){
  hmap <- buildHeatMap(boardKnowledge, NMC)
  #remove known hits
  hmap[which(boardKnowledge==HIT)] <- 0
  
  guessIndex <- which(hmap == max(hmap))[1]
  guessX <- guessIndex %% boardDim[1]
  if(guessX == 0) {guessX <- 10}
  guessY <- floor((guessIndex-1) / boardDim[2])+1
  return(list(c(guessX,guessY), hmap))
}

resultOfGuess <- function(guess){
  x <- guess[1]; y<- guess[2]; returnstr <- ""
  if(trueBoard[x,y] == 0){
    boardKnowledge[x,y] <<- MISS
    returnstr <- "Miss"
  } else{
    #Hit
    returnstr <-"Hit!"
    boatIndex <- abs(trueBoard[x,y])
    trueBoard[x,y] <<- trueBoard[x,y] * -1
    boardKnowledge[x,y] <<- HIT
    if(sum(trueBoard == boatIndex)==0){
      #sunk it
      boardKnowledge[which(trueBoard==(-boatIndex))] <<- -boatIndex
      returnstr <- paste(returnstr," Sunk Boat ",boatIndex,".",sep="")
      shipAlive[boatIndex] <<- FALSE
    } 
    if(sum(trueBoard>0)==0){
      #All Boats Sunk
      returnstr <- paste(returnstr, "You Lose")
    }
  }
  return(returnstr)
}

Test it out. First the setup.

library(fields)
#Define board
boardDim=c(10,10)
#Define ship sizes
shipSizes=c(5,4,3,3,2)
#Initialize ship states
shipAlive = rep(TRUE, length(shipSizes))

#initializeKnowledge
# 0 unknown
# 1,2,3, etc hit boat
boardKnowledge=matrix(data=0, nrow=boardDim[1], ncol=boardDim[2])
trueBoard <- placeAllShips(boardKnowledge)

par(mfrow=c(1,2), mar=c(.5, 3, 4, 0))
image(trueBoard, main="Ship Locations", col=tim.colors(),yaxt="n",xaxt="n"); 
  axis(3, at=seq(0,1, length.out=10), labels=LETTERS[1:10], lwd=0, pos=1, cex.axis=.75)
  axis(2, at=seq(0,1, length.out=10), labels=10:1, lwd=0, pos=0, cex.axis=.75); 
image(boardKnowledge, main="Comp Knowledge", col=tim.colors(),yaxt="n",xaxt="n"); 
  axis(3, at=seq(0,1, length.out=10), labels=LETTERS[1:10], lwd=0, pos=1, cex.axis=.75)
  axis(2, at=seq(0,1, length.out=10), labels=10:1, lwd=0, pos=0, cex.axis=.75)

Play the Game!!!

turn <- 0
while(sum(shipAlive)>0 | turn < 9){
  turn <- turn +1
  guessList <- guessACoord(boardKnowledge,1000)
  guess <- guessList[[1]]
  guessResult <- resultOfGuess(guess)
  print(paste("Turn ",turn,": Computer Guesses ",LETTERS[guess[1]],11-guess[2],": ",guessResult, sep=""))
  
  flush.console()
  par(mfrow=c(1,3), mar=c(.5, 3, 3, 0))
  image(guessList[[2]], main="Heatmap", col=tim.colors(),yaxt="n",xaxt="n"); 
  axis(3, at=seq(0,1, length.out=10), labels=LETTERS[1:10], lwd=0, pos=1)
  axis(2, at=seq(0,1, length.out=10), labels=10:1, lwd=0, pos=0)
  image(trueBoard, main="My Board", col=tim.colors(),yaxt="n",xaxt="n");   
  axis(3, at=seq(0,1, length.out=10), labels=LETTERS[1:10], lwd=0, pos=1)
  axis(2, at=seq(0,1, length.out=10), labels=10:1, lwd=0, pos=0)
  image(boardKnowledge, main="Comp Knowledge", col=tim.colors(),yaxt="n",xaxt="n")
  axis(3, at=seq(0,1, length.out=10), labels=LETTERS[1:10], lwd=0, pos=1)
  axis(2, at=seq(0,1, length.out=10), labels=10:1, lwd=0, pos=0)
}
[1] "Turn 1: Computer Guesses F6: Hit!"

[1] "Turn 2: Computer Guesses E6: Miss"

[1] "Turn 3: Computer Guesses F7: Hit!"

[1] "Turn 4: Computer Guesses F8: Hit! Sunk Boat 3."

[1] "Turn 5: Computer Guesses D5: Miss"

[1] "Turn 6: Computer Guesses G5: Miss"

[1] "Turn 7: Computer Guesses C4: Hit!"

[1] "Turn 8: Computer Guesses C5: Hit!"

[1] "Turn 9: Computer Guesses C3: Hit!"

[1] "Turn 10: Computer Guesses C6: Miss"

[1] "Turn 11: Computer Guesses C2: Hit!"

[1] "Turn 12: Computer Guesses C1: Hit! Sunk Boat 1."

[1] "Turn 13: Computer Guesses D9: Miss"

[1] "Turn 14: Computer Guesses H4: Miss"

[1] "Turn 15: Computer Guesses B7: Hit!"

[1] "Turn 16: Computer Guesses B8: Miss"

[1] "Turn 17: Computer Guesses C7: Miss"

[1] "Turn 18: Computer Guesses B6: Hit!"

[1] "Turn 19: Computer Guesses B5: Hit! Sunk Boat 4."

[1] "Turn 20: Computer Guesses G9: Miss"

[1] "Turn 21: Computer Guesses I3: Miss"

[1] "Turn 22: Computer Guesses F2: Miss"

[1] "Turn 23: Computer Guesses I7: Miss"

[1] "Turn 24: Computer Guesses G1: Miss"

[1] "Turn 25: Computer Guesses J6: Miss"

[1] "Turn 26: Computer Guesses E10: Miss"

[1] "Turn 27: Computer Guesses A4: Miss"

[1] "Turn 28: Computer Guesses E3: Miss"

[1] "Turn 29: Computer Guesses H8: Hit!"

[1] "Turn 30: Computer Guesses H7: Hit!"

[1] "Turn 31: Computer Guesses H6: Hit!"

[1] "Turn 32: Computer Guesses H9: Hit! Sunk Boat 2."

[1] "Turn 33: Computer Guesses F4: Miss"

[1] "Turn 34: Computer Guesses G3: Miss"

[1] "Turn 35: Computer Guesses H2: Miss"

[1] "Turn 36: Computer Guesses I5: Miss"

[1] "Turn 37: Computer Guesses A9: Miss"

[1] "Turn 38: Computer Guesses B3: Miss"

[1] "Turn 39: Computer Guesses D7: Miss"

[1] "Turn 40: Computer Guesses E1: Miss"

[1] "Turn 41: Computer Guesses J4: Miss"

[1] "Turn 42: Computer Guesses E8: Miss"

[1] "Turn 43: Computer Guesses C10: Miss"

[1] "Turn 44: Computer Guesses A2: Miss"

[1] "Turn 45: Computer Guesses I9: Miss"

[1] "Turn 46: Computer Guesses J8: Miss"

[1] "Turn 47: Computer Guesses J2: Hit!"

[1] "Turn 48: Computer Guesses I2: Hit! Sunk Boat 5. You Lose"