From 79726d5ac5947bcefe431e5c3356fe9db565b1eb Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Sun, 15 Dec 2019 16:21:09 +0200
Subject: [PATCH] fsharp(ygosim): feat: added a lot more stuff to normal
 summoning

Signed-off-by: prescientmoon <git@moonythm.dev>
---
 fsharp/ygosim/src/Board.fs   | 82 ++++++++++++++++++++++++++++++------
 fsharp/ygosim/src/Card.fs    |  6 ++-
 fsharp/ygosim/src/Program.fs | 19 +++++++--
 3 files changed, 88 insertions(+), 19 deletions(-)

diff --git a/fsharp/ygosim/src/Board.fs b/fsharp/ygosim/src/Board.fs
index 1c1bee6..d473e55 100644
--- a/fsharp/ygosim/src/Board.fs
+++ b/fsharp/ygosim/src/Board.fs
@@ -3,7 +3,7 @@ module Board
 open FSharpPlus.Lens
 
 module Side =
-    open Card.Card
+    open Card.CardInstance
 
     type Side<'s> =
         { field: CardInstance<'s> option
@@ -31,7 +31,7 @@ module Side =
 
 module Player =
     open Side
-    open Card.Card
+    open Card.CardInstance
 
     type PlayerState =
         | InGame
@@ -94,11 +94,13 @@ module Board =
 
     and Board =
         { players: Player * Player
-          moment: int * Phase }
+          moment: int * Phase
+          lastInstanceId: int }
 
     module Board =
         let inline players f board = f board.players <&> fun v -> { board with players = v }
         let inline moment f board = f board.moment <&> fun v -> { board with moment = v }
+        let inline lastInstanceId f board = f board.lastInstanceId <&> fun v -> { board with lastInstanceId = v }
 
         let inline turn f board = (moment << _1) f board
         let inline phase f board = (moment << _2) f board
@@ -113,13 +115,14 @@ module Board =
         let inline currentPlayerHand f board = (currentPlayer << Player.hand) f board
         let inline currentPlayerLastNormalSummon f board = (currentPlayer << Player.lastNormalSummon) f board
         let inline currentPlayerMonsters f board = (currentPlayer << Player.monsters) f board
+        let inline currentPlayerSide f board = (currentPlayer << Player.side) f board
 
         let inline firstPlayer f board = (players << _1) f board
         let inline secondPlayer f board = (players << _2) f board
 
     type Card = Card.Card<Board>
 
-    type CardInstance = Card.CardInstance<Board>
+    type CardInstance = CardInstance.CardInstance<Board>
 
     type Monster = Card.Monster<Board>
 
@@ -131,28 +134,52 @@ module Board =
 
     let emptyBoard =
         { players = (initialPlayer 8000 0, initialPlayer 8000 1)
-          moment = 0, Draw }
+          moment = 0, Draw
+          lastInstanceId = -1 }
+
+    let instantiate board card =
+        let instance =
+            { CardInstance.template = card
+              CardInstance.id = board.lastInstanceId }
+
+        (instance, over Board.lastInstanceId <| (+) 1 <| board)
 
 module Client =
     open Player
     open Turn
+    open Board
+    open Utils
 
     type Log =
         | CardToHand of string
+        | MonsterSummoned of Monster * int
         | NewPhase of Phase
         | StateChanged of PlayerState * PlayerState
         | ChooseZone of int list
+        | ChooseMonster of Monster list
 
     type Client = Log -> int
 
     let rec chooseZone client free =
-        let freeIndices = List.mapi (fun i _ -> i) free
-        let command = ChooseZone freeIndices
-        let result = client command
+        let result =
+            free
+            |> List.toIndices
+            |> ChooseZone
+            |> client
 
-        if List.contains result freeIndices then free.[result]
+        if List.containsIndex result free then result
         else chooseZone client free
 
+    let rec chooseMonster client monsters =
+        let result =
+            monsters
+            |> List.map (fun (m, _) -> m)
+            |> ChooseMonster
+            |> client
+
+        if List.containsIndex result monsters then monsters.[result]
+        else chooseMonster client monsters
+
 module Zone =
     open Player
     open Side
@@ -166,13 +193,16 @@ module Zone =
 
 module Summon =
     open Card.Card
+    open Card.Monster
+    open Card.CardInstance
     open Card
     open Board
     open Zone
     open Client
+    open Utils
 
     module Normal =
-        let inline numberOfTributes (monster: Monster) =
+        let numberOfTributes (monster: Monster) =
             let level = monster ^. Card.level
 
             if level <= 4 then 0
@@ -191,23 +221,45 @@ module Summon =
 
             requiredTributes <= possibleTributes && freeZones > 0
 
-
-        let hasNormalSummonableMonster board =
+        let normalSummonable board =
             let hand = board ^. Board.currentPlayerHand
             let monsters = List.choose monster hand
 
-            List.exists <| isNormalSummonable board <| monsters
+            let isSummonable = view _1 >> isNormalSummonable board
+
+            List.filter isSummonable monsters
+
+        let hasNormalSummonableMonster =
+            (normalSummonable
+             >> List.length
+             >> (<=) 1)
 
         let canNormalSummon board =
             hasNormalSummonableMonster board && board ^. Board.currentPlayerLastNormalSummon < board ^. Board.turn
 
         let performNormalSummon client board =
+            let summonable = normalSummonable board
+            let target = chooseMonster client summonable
+            let (_, _id) = target
+
             let free = freeMonsterZones <| board ^. Board.currentPlayer
             let zone = chooseZone client free
 
             let turn = board ^. Board.turn
 
-            board |> Board.currentPlayerLastNormalSummon .-> turn
+            let summonedInstance =
+                target
+                |> toCardInstance
+                |> Some
+
+            let removeTarget = List.filter (fun card -> card.id <> _id)
+
+            client <| MonsterSummoned(target ^. _1, zone) |> ignore
+
+            board
+            |> over Board.currentPlayerHand removeTarget
+            |> (Board.currentPlayerMonsters << Lens.indexToLens zone) .-> summonedInstance
+            |> Board.currentPlayerLastNormalSummon .-> turn
 
 module Game =
     open Turn
@@ -291,5 +343,7 @@ module Game =
         if currentState <> InGame then
             let newStates = getPlayerStates newBoard
             client <| StateChanged newStates |> ignore
+
+            newBoard
         else
             game <| switchPhases client newBoard <| client
diff --git a/fsharp/ygosim/src/Card.fs b/fsharp/ygosim/src/Card.fs
index 392d540..a6f27a7 100644
--- a/fsharp/ygosim/src/Card.fs
+++ b/fsharp/ygosim/src/Card.fs
@@ -130,11 +130,14 @@ module Card =
 
         let inline level f card = (_2 << MonsterCardDetails.level) f card
 
+
+module CardInstance =
+    open Card
+
     type CardInstance<'s> =
         { template: Card<'s>
           id: int }
 
-
     module CardInstance =
         let inline template f card = f card.template <&> fun v -> { card with template = v }
         let inline _id f card = f card.id <&> fun v -> { card with id = v }
@@ -142,6 +145,7 @@ module Card =
 
 module Monster =
     open Card
+    open CardInstance
 
     let monster card: option<Monster<'s> * int> =
         match card.template with
diff --git a/fsharp/ygosim/src/Program.fs b/fsharp/ygosim/src/Program.fs
index fcd9e47..1c2bf04 100644
--- a/fsharp/ygosim/src/Program.fs
+++ b/fsharp/ygosim/src/Program.fs
@@ -14,7 +14,7 @@
 
     [<EntryPoint>]
     let main _ =
-        let sampleCard = 
+        let sampleCardTemplate = 
             Monster ({ name= "sampleCard" 
                        text="something"
                        effects = []}
@@ -24,8 +24,10 @@
                        attribute = Fire
                        race = Warrior })
 
-                  
-        let board = over Board.secondPlayer <| toDeckBottom sampleCard <| emptyBoard
+
+        let (sampleCard, initialBoard) = instantiate emptyBoard sampleCardTemplate
+                 
+        let board = over Board.firstPlayer <| toDeckBottom sampleCard <| initialBoard
 
         let client action =
             match action with
@@ -41,11 +43,20 @@
                 let i = System.Console.ReadLine() |> int
 
                 free.[i]
+            | ChooseMonster monsters ->
+                printfn "What monster do you want to choose? %A" <| List.map (fun (_base, details) -> _base.name) monsters
+                let i = System.Console.ReadLine() |> int
+
+                i
+            | MonsterSummoned (card, zone) -> 
+                printfn "Monster %A was summoned in zone %i" card zone
+                0
             | _ -> 
                 printfn "Something unkown happened" 
                 0
 
-        game board client
+        let finalBoard = game board client
 
+        printfn "The final baord was: %A" finalBoard
 
         0 // return integer code