Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Smarter AI #13

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions AI.elm
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
module AI where
module AI (shoot) where

-- Core
import Debug
import Array
import Random

-- 3rd party
import Matrix
-- Battleship
import Player
import Grid
import Ship

randomShot : Int -> Player.Player -> Player.Player -> (Player.Player, Player.Player)
randomShot seed player enemy =
Expand All @@ -25,3 +26,11 @@ randomShot seed player enemy =
Player.shoot pos player enemy
Nothing ->
(player, enemy)

shoot : Int -> Player.Player -> Player.Player -> (Player.Player, Player.Player)
shoot seed player enemy =
case Grid.nextShot player.trackingGrid of
Just (x,y) ->
Player.shoot (Debug.log "nextShot position:" (x,y)) player enemy
Nothing ->
randomShot (Debug.log "" seed) player enemy
2 changes: 1 addition & 1 deletion Fleet.elm
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ addShip ship fleet =
length = List.length <| toList fleet
in
fleet
|> Dict.insert length { ship | id <- length }
|> Dict.insert length { ship | id = length }

getShip : Int -> Fleet -> Maybe Ship.Ship
getShip shipId fleet =
Expand Down
64 changes: 61 additions & 3 deletions Grid.elm
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module Grid
( Grid
, Cell
, Context
, toHtml
, emptyPrimaryGrid
Expand All @@ -14,11 +15,13 @@ module Grid
, getHeight
, getWidth
, getUnknownPositions
, nextShot
, shipInBounds
) where

-- Core
import Array -- For matrix conversion
import Debug
-- Evan
import Html
import Html.Attributes
Expand Down Expand Up @@ -98,13 +101,68 @@ getUnknownPositions grid =
|> Array.toList
|> List.map (\(y,x) -> (x,y))

-- AI helper
nextShot : Grid -> Maybe Loc.Location
nextShot grid =
let
hitCellLoc =
grid
|> Matrix.toIndexedArray
|> Array.foldl (\((x,y), cell) hitLoc ->
if hitLoc == Nothing && cell == (Ship True) then
Just (x,y) else hitLoc)
Nothing
isEmptyHit (x,y) =
case Matrix.get x y grid of
Just cell ->
cell == (Empty True)
Nothing ->
False
isUnkown (x,y) =
case Matrix.get x y grid of
Just cell ->
cell == Unknown
Nothing ->
False
isHit (x,y) =
case Matrix.get x y grid of
Just cell ->
cell == (Ship True)
Nothing ->
False
findUnknown (x,y) =
let
right = (x+1,y)
bottom = (x,y+1)
left = (x-1,y)
top = (x,y-1)
in
if isHit right && isEmptyHit left then findUnknown right
else if isHit right && isHit left then findUnknown right
else if isHit right then left -- FIXME
else if isHit bottom && isEmptyHit top then findUnknown bottom
else if isHit bottom && isHit top then findUnknown bottom
else if isHit bottom then top -- FIXME
else if isUnkown left then left
else if isUnkown right then right
else if isUnkown bottom then bottom
else if isUnkown top then top
else bottom
in
case hitCellLoc of
Just loc ->
Just <| (\(a,b) -> (b,a)) <| findUnknown loc
Nothing ->
Nothing

shoot : Loc.Location -> Grid -> Cell
shoot (row, col) grid =
case Matrix.get col row grid of
Just cell ->
case cell of
Ship _ -> Ship True
Empty _ -> Empty True
_ -> cell -- Is still this ok to do?
Nothing -> -- Error
Empty False

Expand Down Expand Up @@ -175,7 +233,7 @@ cellToHtml hoverClick y x cell =
box color = Html.div
([ Html.Attributes.style <| ("background-color", color) :: style
, Html.Attributes.class "cell"
] ++ adm) []
] ++ adm) [{-Html.text (toString pos)-}]
in
case cell of
Ship isHit ->
Expand Down Expand Up @@ -224,8 +282,8 @@ toHtml context grid =
in
Html.div
([ Html.Attributes.class "battlefield"
, Html.Attributes.style []
] ++ event)
, Html.Attributes.style ["display" := "inline-block"]
] ++ event)
(grid
|> Matrix.indexedMap (cellToHtml context)
|> toHtmlRows)
66 changes: 34 additions & 32 deletions Main.elm
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ module Battleship (main) where
import String
import Keyboard
import Time
import Debug
-- Evan
import Html
import Html.Attributes
Expand Down Expand Up @@ -80,7 +81,9 @@ wrapper : List Html.Html -> Html.Html
wrapper htmlList =
Html.main'
[ Html.Attributes.style
[ "display" := "flex"
[
--"display" := "flex"
"text-align" := "center"
, "flex-direction" := "row"
, "align-items" := "center"
, "margin" := "50px 0px"
Expand Down Expand Up @@ -113,22 +116,20 @@ setupView address model selectedShipId =
}
shipSelector = Html.div
[ Html.Attributes.style
[ "display" := "flex"
[ "display" := "inline-flex"
, "overflow" := "hidden"
, "border-radius" := "10px"
]
] <| List.map (shipFieldView address selectedShipId) (Player.getShips model.player)
shipSetup = Html.div []
[ shipSelector, hint ]
hint = Html.div
[ Html.Attributes.style ["margin" := "20px 0px"] ]
[ Html.text "Press \"D\" to change ship's orientation" ]
shipSetup = Html.div
[ Html.Attributes.style
[ "text-align" := "center" ]
]
[ hint, shipSelector ]
in
[ shipSetup
, Player.previewShip hoverClick model.hoverPos selectedShipId model.player
, Player.viewTrackingGrid Nothing model.computer
]

playView : Signal.Address Action -> Model -> List Html.Html
Expand Down Expand Up @@ -234,17 +235,17 @@ update action model =
if Player.allShipsAdded model.computer then
model
else
{ model | computer <- Player.random seed }
{ model | computer = Player.random seed }
SetupOrientationToggle ->
case model.selectedShipId of
Just shipId ->
{ model | player <- Player.updateShip shipId Ship.toggleOrientation model.player }
{ model | player = Player.updateShip shipId Ship.toggleOrientation model.player }
Nothing ->
model
SetupSelectShip shipId ->
{ model | selectedShipId <- shipId }
{ model | selectedShipId = shipId }
SetupShowShip maybePos ->
{ model | hoverPos <- maybePos }
{ model | hoverPos = maybePos }
SetupAddShip pos ->
case model.selectedShipId of
Just shipId ->
Expand All @@ -256,11 +257,11 @@ update action model =
nextShipId = Player.nextNotAddedShipId newPlayer
in
{ model |
player <- newPlayer,
computer <- Player.random model.seed,
selectedShipId <- nextShipId,
player = newPlayer,
computer = Player.random model.seed,
selectedShipId = nextShipId,
-- If nextShipId is `Nothing`, It's time to `Play`
state <- if nextShipId == Nothing then Play else model.state
state = if nextShipId == Nothing then Play else model.state
}
Nothing ->
model
Expand All @@ -269,25 +270,26 @@ update action model =
PlayShoot pos ->
let
(player, computer) = Player.shoot pos model.player model.computer
(newComputer, newPlayer) = AI.randomShot model.seed computer player
(newComputer, newPlayer) = AI.shoot model.seed computer player
--p = Debug.log "Shoot position" pos
in
if | Player.allShipsSunk computer ->
{ model |
player <- player,
computer <- computer,
state <- GameOver Player1
}
| Player.allShipsSunk newPlayer ->
{ model |
player <- newPlayer,
computer <- newComputer,
state <- GameOver Player2
}
| otherwise ->
if Player.allShipsSunk computer then
{ model |
player <- newPlayer,
computer <- newComputer
player = player,
computer = computer,
state = GameOver Player1
}
else if Player.allShipsSunk newPlayer then
{ model |
player = newPlayer,
computer = newComputer,
state = GameOver Player2
}
else
{ model |
player = newPlayer,
computer = newComputer
}
UpdateSeed seed ->
{ model | seed <- seed }
{ model | seed = seed }
NoOp -> model
46 changes: 21 additions & 25 deletions Player.elm
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ addShip shipId player =
{ player |
-- This is important here. Both the ship in the fleet and the grid
-- are updated when a ship is added.
fleet <- Fleet.updateShip shipId Ship.setAddedTrue player.fleet,
primaryGrid <- Grid.addShip ship player.primaryGrid
fleet = Fleet.updateShip shipId Ship.setAddedTrue player.fleet,
primaryGrid = Grid.addShip ship player.primaryGrid
}
else
player
Expand All @@ -98,18 +98,20 @@ updateShip shipId fn player =
let
newShip = Fleet.updateShip shipId fn player.fleet
in
{ player | fleet <- newShip }
{ player | fleet = newShip }

canAddShip : Ship.Ship -> Player -> Bool
canAddShip ship player =
-- order here is important for optimization. `shipInBounds` is cheap
if | not (Grid.shipInBounds ship player.primaryGrid) -> False
| Fleet.shipOverlaps ship player.fleet -> False
| otherwise -> True
if not (Grid.shipInBounds ship player.primaryGrid) then
False
else if Fleet.shipOverlaps ship player.fleet then
False
else True

updateGrid : Grid.Grid -> Player -> Player
updateGrid grid player =
{ player | primaryGrid <- grid }
{ player | primaryGrid = grid }

getShips : Player -> List Ship.Ship
getShips player =
Expand Down Expand Up @@ -147,29 +149,23 @@ shoot pos player enemy =
else grid
Nothing -> grid
in
(,) { player | trackingGrid <- updateIfSunk trackingGrid }
{ enemy | primaryGrid <- updateIfSunk primaryGrid }
(,) { player | trackingGrid = updateIfSunk trackingGrid }
{ enemy | primaryGrid = updateIfSunk primaryGrid }

previewShip : Maybe Grid.Context -> Maybe Loc.Location -> Maybe Int -> Player -> Html.Html
previewShip clickHover maybeHoverPos maybeShipId player =
let
noPreview =
Html.div []
[ player.primaryGrid
|> Grid.toHtml clickHover
]
player.primaryGrid
|> Grid.toHtml clickHover
preview ship =
Html.div []
[ player.primaryGrid
|> Grid.addShip ship
|> Grid.toHtml clickHover
]
player.primaryGrid
|> Grid.addShip ship
|> Grid.toHtml clickHover
invalid ship =
Html.div []
[ player.primaryGrid
|> Grid.addInvalidShip ship
|> Grid.toHtml clickHover
]
player.primaryGrid
|> Grid.addInvalidShip ship
|> Grid.toHtml clickHover
in
case maybeShipId of
Nothing -> noPreview
Expand All @@ -190,8 +186,8 @@ previewShip clickHover maybeHoverPos maybeShipId player =

viewTrackingGrid : Maybe Grid.Context -> Player -> Html.Html
viewTrackingGrid context player =
Html.div [] [ Grid.toHtml context player.trackingGrid ]
Grid.toHtml context player.trackingGrid

viewPrimaryGrid : Maybe Grid.Context -> Player -> Html.Html
viewPrimaryGrid context player =
Html.div [] [ Grid.toHtml context player.primaryGrid ]
Grid.toHtml context player.primaryGrid
Loading