/// <summary> /// Handles click events on this placement piece. /// </summary> public void OnMouseUpAsButton() { if (!placed) { // Piece shouldn't be null Debug.Assert(piece != null); // Selected this piece for placement if (turnHandler.GetCurrentPlayer().colour == piece.colour && turnHandler.state == TurnHandler.State.Placement) { turnHandler.PlacementPiece = this; turnHandler.UnhighlightPlacementTiles(); RemovePlacementArrows(); // Highlight the available placement tiles foreach (TileNode placementTile in turnHandler.PlacementTiles) { if ((placementTile.edgeId + 3) % 6 == piece.direction && placementTile.tower == null) { placementTile.highlight(); } } } } }
private void OnGenFinished(WorldMap world) { foreach (Vector2Int position in world.mapDict.Keys) { GameTile newGameTile = new GameTile(); newGameTile.armies = 0; newGameTile.type = world.mapDict[position]; if (newGameTile.type != null) { gameMap.Add(position, newGameTile); } WorldRenderer.RenderWorld(gameMap); } remainingPlayers = new List <Player>(startingPlayers); PlayerSpawner.SpawnPlayers(remainingPlayers, 4); UpdateWorldRender(); HUDManager.SetPlayerIndicator(TurnHandler.GetCurrentPlayer()); OnSetupComplete?.Invoke(); setupHasFinished = true; }
public void RegisterClickOnTile(Vector2Int tilePos) { GameTile tile = GameController.instance.GetTile(tilePos); if (tile == null) { return; } // Verify that this is the current player's tile if (!firstTileSelected && tile.owner != TurnHandler.GetCurrentPlayer()) { return; } if (!firstTileSelected) { if (tile == null) { return; } // Verify that this is the current player's tile if (tile.owner != TurnHandler.GetCurrentPlayer()) { return; } FirstSelectedTile = new Vector3Int(tilePos.x, tilePos.y, 0); firstTileSelected = true; if (!placingArmies) { SelectionDisplayer.PlaceSelectionRing(new Vector2Int(FirstSelectedTile.x, FirstSelectedTile.y)); } if (placingArmies) { if (TurnHandler.GetCurrentPlayer().armiesToPlace <= 0) { placingArmies = false; } else { GameController.instance.PlaceArmyIfAvailable(TurnHandler.GetCurrentPlayer(), new Vector2Int(tilePos.x, tilePos.y)); firstTileSelected = false; secondTileSelected = false; if (TurnHandler.GetCurrentPlayer().armiesToPlace <= 0) { placingArmies = false; HUDManager.HidePlaceArmyText(); } else { HUDManager.ShowPlaceArmyText(TurnHandler.GetCurrentPlayer().armiesToPlace); } return; } } } else { SecondSelectedTile = new Vector3Int(tilePos.x, tilePos.y, 0); secondTileSelected = true; } if (firstTileSelected && secondTileSelected) { if (Mathf.Abs(FirstSelectedTile.x - SecondSelectedTile.x) == 1 && Mathf.Abs(FirstSelectedTile.y - SecondSelectedTile.y) == 0 || Mathf.Abs(FirstSelectedTile.x - SecondSelectedTile.x) == 0 && Mathf.Abs(FirstSelectedTile.y - SecondSelectedTile.y) == 1) { if (GameController.instance.GetTile(new Vector2Int(FirstSelectedTile.x, FirstSelectedTile.y)).owner == TurnHandler.GetCurrentPlayer()) { if (GameController.instance.GetTile(new Vector2Int(SecondSelectedTile.x, SecondSelectedTile.y)).owner == TurnHandler.GetCurrentPlayer()) { // Both tiles are the current player's // Perform troop movement GameController.instance.MoveArmies(new Vector2Int(FirstSelectedTile.x, FirstSelectedTile.y), new Vector2Int(SecondSelectedTile.x, SecondSelectedTile.y)); } else if (GameController.instance.GetTile(new Vector2Int(SecondSelectedTile.x, SecondSelectedTile.y)).armies == 0) { // Move into empty territory GameController.instance.MoveArmies(new Vector2Int(FirstSelectedTile.x, FirstSelectedTile.y), new Vector2Int(SecondSelectedTile.x, SecondSelectedTile.y)); } else { // Player attacking enemy GameController.instance.LaunchAttack(new Vector2Int(FirstSelectedTile.x, FirstSelectedTile.y), new Vector2Int(SecondSelectedTile.x, SecondSelectedTile.y)); } } } firstTileSelected = false; secondTileSelected = false; SelectionDisplayer.ClearSelectionRing(); } }
static IEnumerator Train(int numGenerations, int modelsPerGen) { Dictionary <Vector2Int, GameTile> gameMap = GameController.instance.gameMap; int inputNodeCount = gameMap.Keys.Count * 3; int outputNodeCount = 1 + gameMap.Keys.Count * 2; int nodeCountPerLayer = inputNodeCount; int[] perceptronArray = new int[HIDDEN_LAYER_COUNT]; for (int i = 0; i < perceptronArray.Length; i++) { // Have the number of perceptrons in each layer equal to number of inputs perceptronArray[i] = nodeCountPerLayer; } if (modelsPerGen % 2 != 0) { // Make sure the number of models per generation is even so we always // have two models to pit against each other Debug.LogWarning("Number of models per gen is odd. Fixing."); modelsPerGen++; } NeuralNetwork nn = new NeuralNetwork(inputNodeCount, outputNodeCount, perceptronArray); float[][][][] genWeights = new float[modelsPerGen][][][]; // Randomize weights for (int m = 0; m < modelsPerGen; m++) { genWeights[m] = new float[HIDDEN_LAYER_COUNT][][]; for (int l = 0; l < HIDDEN_LAYER_COUNT; l++) { genWeights[m][l] = new float[nodeCountPerLayer][]; for (int p = 0; p < nodeCountPerLayer; p++) { int weightCount = nn.GetInputCount(l, p); genWeights[m][l][p] = new float[weightCount]; for (int w = 0; w < weightCount; w++) { genWeights[m][l][p][w] = Random.Range(WEIGHT_INIT_MIN, WEIGHT_INIT_MAX); } } } } // TRAINING PROCESS // We'll be testing two models at a time, pitted against each other. for (int gen = 0; gen < numGenerations; gen++) { // One bool for each model in the current generation, to track whether it won bool[] wins = new bool[modelsPerGen]; for (int model = 0; model < modelsPerGen; model += 2) { SceneManager.UnloadSceneAsync(MAIN_SCENE_INDEX); while (SceneManager.GetSceneByBuildIndex(MAIN_SCENE_INDEX).isLoaded) { yield return(null); } SceneManager.LoadScene(MAIN_SCENE_INDEX, LoadSceneMode.Additive); yield return(null); SceneManager.SetActiveScene(SceneManager.GetSceneByBuildIndex(MAIN_SCENE_INDEX)); if (!GameController.instance.setupHasFinished) { Debug.Log("Waiting for setup"); bool setupFinished = false; GameController.instance.OnSetupComplete += (() => setupFinished = true); while (setupFinished == false) { // TODO timeout yield return(null); } } Debug.Log("Setup finished."); // Wait a frame for scene to load yield return(null); float[] model1LastInput = new float[inputNodeCount]; float[] model2LastInput = new float[inputNodeCount]; // Play through a game for every two models while (GameController.instance.gameEnded == false) { if (TurnHandler.CurrentTurn > MAX_TURN_COUNT) { Debug.Log("Turn limit reached."); // Hit max turn count; end the game GameController.instance.gameEnded = true; break; } // Set weights depending on whose turn it is if (TurnHandler.CurrentTurn % 2 == 1) // Turn is odd (turns start on 1) { // Set NN to have weights for first model nn.SetWeights(genWeights[model]); } else // Turn is even { // Set NN to have weights for second model nn.SetWeights(genWeights[model + 1]); } float[] input = GatherInputs(); float[] output = nn.Calculate(input); while (true) // Repeat until turn end { // Find best starting tile int bestStart = 0; for (int i = 0; i < GameController.WORLD_X * GameController.WORLD_Y; i++) { int y = (i) / GameController.WORLD_X; int x = (i) % GameController.WORLD_X; // If this would not be a valid start tile, make it highly undesirable GameTile tile = GameController.instance.gameMap[new Vector2Int(x, y)]; if (tile.owner != TurnHandler.GetCurrentPlayer() || (!InputHandler.instance.placingArmies && tile.armies == tile.expendedArmies)) { output[i + 1] -= 1000; } if (output[i + 1] > output[bestStart + 1]) { bestStart = i; } } // Find best target tile int offset = 1 + GameController.WORLD_X * GameController.WORLD_Y; int bestTarget = 0; for (int i = 0; i < GameController.WORLD_X * GameController.WORLD_Y; i++) { int y = (i) / GameController.WORLD_X; int x = (i) % GameController.WORLD_X; GameTile tile = GameController.instance.gameMap[new Vector2Int(x, y)]; // Check if this tile is next to the start tile, and if not make it highly undesirable bool nextToStartTile = false; foreach (Vector2Int adjTile in GameController.instance.GetAdjacentTiles(new Vector2Int(x, y))) { if (adjTile.x == bestStart % GameController.WORLD_X && adjTile.y == bestStart / GameController.WORLD_X) { nextToStartTile = true; } } if (!nextToStartTile) { output[i + offset] -= 1000; } if (output[i + offset] > output[bestTarget + offset]) { bestTarget = i; } } if (output[0] > output[bestStart + 1]) { // Agent decided end turn is best move break; } else { // Input move int startY = bestStart / GameController.WORLD_X; int startX = bestStart % GameController.WORLD_X; int targetY = bestTarget / GameController.WORLD_X; int targetX = bestTarget % GameController.WORLD_X; InputHandler.instance.ClearTileSelection(); InputHandler.instance.RegisterClickOnTile(new Vector2Int(startX, startY)); if (DoShowMoves) { yield return(null); } if (!InputHandler.instance.placingArmies) // Only input the second move if we're not placing armies { InputHandler.instance.RegisterClickOnTile(new Vector2Int(targetX, targetY)); if (DoShowMoves) { yield return(null); } } } if (GameController.instance.gameEnded) { break; } input = GatherInputs(); if (!Enumerable.SequenceEqual(input, GatherInputs())) { Debug.Log(input[1]); Debug.Log(GatherInputs()[1]); Debug.LogError("input gathering is not deterministic"); } output = nn.Calculate(input); } InputHandler.instance.OnEndTurnButton(); } // The game is over. // Evaluate which player won: if (ScorePosition(GameController.instance.startingPlayers[0]) > ScorePosition(GameController.instance.startingPlayers[1])) { Debug.Log("Win goes to " + GameController.instance.startingPlayers[0].nationName); wins[model] = true; wins[model + 1] = false; } else if (ScorePosition(GameController.instance.startingPlayers[0]) < ScorePosition(GameController.instance.startingPlayers[1])) { Debug.Log("Win goes to " + GameController.instance.startingPlayers[1].nationName); wins[model] = false; wins[model + 1] = true; } else { Debug.Log("A perfect draw."); wins[model] = false; wins[model + 1] = false; } } // All the models in this generation have been tested. Time to breed. Debug.Log("Breeding winners of generation " + gen); List <float[][][]> breedingPool = new List <float[][][]>(); for (int m = 0; m < modelsPerGen; m++) { if (wins[m] == true) { breedingPool.Add(genWeights[m]); } } if (breedingPool.Count == 0) { breedingPool.Add(genWeights[0]); Debug.LogError("No models in gen " + gen + " survived to breed!"); } for (int m = 0; m < modelsPerGen; m++) { for (int l = 0; l < HIDDEN_LAYER_COUNT; l++) { for (int p = 0; p < nodeCountPerLayer; p++) { for (int w = 0; w < genWeights[m][l][p].Length; w++) { if (Random.value < MUTATION_CHANCE) { genWeights[m][l][p][w] = Random.Range(WEIGHT_INIT_MIN, WEIGHT_INIT_MAX); } else { int i = Random.Range(0, breedingPool.Count); genWeights[m][l][p][w] = breedingPool[i][l][p][w]; } } } } } } }
static float[] GatherInputs() { Dictionary <Vector2Int, GameTile> gameMap = GameController.instance.gameMap; int inputNodeCount = gameMap.Keys.Count * 3; float[] inputs = new float[inputNodeCount]; Vector2Int[] mapKeys = new Vector2Int[gameMap.Keys.Count]; gameMap.Keys.CopyTo(mapKeys, 0); System.Array.Sort(mapKeys, MathHelper.Vector2IntCompare); for (int i = 0; i < mapKeys.Length; i++) { switch (gameMap[mapKeys[i]].type.id) { case "water": inputs[i] = 0; break; case "plains": inputs[i] = 1; break; case "forest": inputs[i] = 2; break; case "farm": default: inputs[i] = 3; break; } } int baseIndex = mapKeys.Length; for (int i = 0; i < mapKeys.Length; i++) { if (gameMap[mapKeys[i]].owner == null) { inputs[i + baseIndex] = 0; // unoccupied } else if (gameMap[mapKeys[i]].owner == TurnHandler.GetCurrentPlayer()) { inputs[i + baseIndex] = 1; // friendly territory } else { inputs[i + baseIndex] = 2; // enemy territory } } baseIndex = mapKeys.Length * 2; for (int i = 0; i < mapKeys.Length; i++) { inputs[i + baseIndex] = gameMap[mapKeys[i]].armies; } return(inputs); }
//Recives event to play card, updating deck on all clients public void HandlePhotonEvents(EventData photonEvent) { byte eventCode = photonEvent.Code; switch (eventCode) { case 1: //------Playing Cards { if (view.IsMine) { //Stores the number of each trick card where the number matters byte[] cards = (byte[])photonEvent.CustomData; //A count of each trick card played //Aces = index[0] 2s = index[2] | 8s = index[2] | Jacks = index[3] | Black Queens = index[4] | Kings of Hearts = index[5] byte[] trickCards = new byte[6]; #region Trick Card reading //Get a count of each trick card in the cards played foreach (byte id in cards) { Card card = deck.FindCard(id); switch (card.GetValue()) { //Ace case 1: trickCards[0]++; break; //Twos case 2: trickCards[1]++; break; //Eights case 8: turnHandler.PlayerUseTurn(); trickCards[2]++; break; //Jacks case 11: turnHandler.ReverseOrder(); trickCards[3]++; break; //Black Queens case 12: if (card.GetCardId() == 102) { lights.DiscoMode(); } break; //King of Hearts case 13: if (card.GetSuit() == 1) { trickCards[5]++; } break; } } twoCount += trickCards[1]; kingCount += trickCards[5]; //Let player set ace value if (trickCards[0] > 0 && turnHandler.GetCurrentPlayer() == view.ViewID) { playerHud.aceSelectionArea.GetComponent <CanvasGroup>().alpha = 1; } //Only use turn if jacks havn't reversed the order if (trickCards[3] % 2 == 0 || trickCards[3] == 0) { turnHandler.PlayerUseTurn(); } #endregion deck.PlayCard(cards); playerHud.topCardPrompt.GetComponent <Image>().sprite = deck.GetPlayDeckTopCard().GetCardSprite(); } } break; case 2: //------Game Start { if (view.IsMine) { //Shuffles all players decks using the same seed, meaning all players will have the same randomization byte seed = (byte)photonEvent.CustomData; deck.Shuffle(seed); deck.PlayFirstCard(); //Enables all UI buttons on game start playerHud.drawCardsButton.interactable = true; playerHud.sortCardsButton.interactable = true; playerHud.believeButton.interactable = true; playerHud.lastCardButton.interactable = true; gameStartButton.gameObject.SetActive(false); //Adds all the current players to the turn handler to be sorted through turnHandler.AddPlayers(); } } break; case 3: //------Drawing Cards { if (view.IsMine) { if ((int)photonEvent.CustomData != view.ViewID) { Card card = deck.DrawCard(); } if ((int)photonEvent.CustomData == turnHandler.GetCurrentPlayer()) { turnHandler.PlayerUseTurn(); } } } break; case 4: //------Game Start Card Dealing { int eventViewID = (int)photonEvent.CustomData; if (eventViewID == view.ViewID && view.IsMine) { DrawMultipleCards(5); DealStartCardsLoop(); } } break; case 5: //------Game Start Card Loop { DealStartCards(); } break; case 6: //------Set trick cards to be used { twoCount = 0; kingCount = 0; } break; case 7: //------Update game log { if (view.IsMine) { string text = (string)photonEvent.CustomData; playerHud.SendChatboxMessage(text); } } break; case 8: //------Handle Game Over { if (view.IsMine) { string[] message = (string[])photonEvent.CustomData; playerHud.gameObject.SetActive(false); GameObject winnerScreen = GameObject.Find("WinnerScreen"); winnerScreen.GetComponent <CanvasGroup>().alpha = 1; string gameOverMessage; //If someone won... if (message[1] == "Winner") { //If they were also first winner, display flawless message if (firstWinner == message[0]) { gameOverMessage = winnerScreen.GetComponentInChildren <Text>().text = message[0] + "\n is flawless!"; } //Else just display winner message else { gameOverMessage = winnerScreen.GetComponentInChildren <Text>().text = message[0] + "\n is the winner!"; } } //If the deck ran out of cards... else { gameOverMessage = winnerScreen.GetComponentInChildren <Text>().text = message[0] + "\n ruined the game..."; } //Print the winner, time and game log to file string path = "GameLog.txt"; StreamWriter writer = new StreamWriter(path, true); writer.WriteLine(DateTime.Now.ToString()); writer.WriteLine(playerHud.chatBoxText.GetComponent <Text>().text); writer.WriteLine(gameOverMessage); writer.WriteLine("----------------------------< Game End >----------------------------"); writer.Close(); //Move back to the main menu StartCoroutine("ChangeScene"); } } break; case 9: //------Set first winner { firstWinner = (string)photonEvent.CustomData; } break; case 10: //Blind play / belief button { if (view.IsMine) { Card card = deck.DrawCard(); } } break; case 11: //Handle Black queen { } break; case 12: //Recieving your cards from a black queen target { short[] data = (short[])photonEvent.CustomData; if (data[0] == view.ViewID) { for (int i = 1; i < data.Length; i++) { Card temp = deck.FindCard((byte)data[i]); myCards.Add(temp.GetCardId(), temp); } } } break; } }