private void TakeShot(object sender, EventArgs e) { // consider adding a check for type in the future to made it more modular Label selected = (Label)sender; // create a Coords for the player shot and send it to the opponent if (selected.BackColor == Color.DodgerBlue) { Coords playerShot = new Coords(OpponentBoard.GetRow(selected), OpponentBoard.GetColumn(selected)); int result = AI.ResolveShot(playerShot); UpdateLabel(true, result); // if a miss, chance to miss color, otherwise turn hit color and check for sunk if (result == 0) { selected.BackColor = Color.AliceBlue; } else { selected.BackColor = Color.Red; // adjust the ship labels if a ship was sunk if (result > 1) { switch (result) { case 2: OpponentPTBoatLabel.Font = new Font(OpponentPTBoatLabel.Font, FontStyle.Strikeout); break; case 3: OpponentSubmarineLabel.Font = new Font(OpponentSubmarineLabel.Font, FontStyle.Strikeout); break; case 4: OpponentDestroyerLabel.Font = new Font(OpponentDestroyerLabel.Font, FontStyle.Strikeout); break; case 5: OpponentBattleshipLabel.Font = new Font(OpponentBattleshipLabel.Font, FontStyle.Strikeout); break; case 6: OpponentCarrierLabel.Font = new Font(OpponentCarrierLabel.Font, FontStyle.Strikeout); break; default: break; } } } } // if the have already selected that space it will not be the normal color and we can discout their click else { return; } if (AI.CheckLose()) { MessageBox.Show("You won!", "Victory!"); Close(); } // now the opponent shoots at us **temporary code *** Coords opponentShot = AI.TakeTurn(); int shotResult = ResolveShot(opponentShot); AI.Report(shotResult); UpdateLabel(false, shotResult); Control label = BoardTable.GetControlFromPosition(opponentShot.x, opponentShot.y); // if a miss, chance to miss color, otherwise turn hit color and check for sunk if (shotResult == 0) { label.BackColor = Color.AliceBlue; } else { label.BackColor = Color.Red; // adjust the ship labels if a ship was sunk if (shotResult > 1) { switch (shotResult) { case 2: PlayerPTBoatLabel.Font = new Font(PlayerPTBoatLabel.Font, FontStyle.Strikeout); break; case 3: PlayerSubmarineLabel.Font = new Font(PlayerSubmarineLabel.Font, FontStyle.Strikeout); break; case 4: PlayerDestroyerLabel.Font = new Font(PlayerDestroyerLabel.Font, FontStyle.Strikeout); break; case 5: PlayerBattleshipLabel.Font = new Font(PlayerBattleshipLabel.Font, FontStyle.Strikeout); break; case 6: PlayerCarrierLabel.Font = new Font(PlayerCarrierLabel.Font, FontStyle.Strikeout); break; default: break; } } } if (shipsSunk > 4) { MessageBox.Show("You lost, better luck next time", "Defeat"); Close(); } }
// reverses the direction modifier in case we need to swap private void reverseDirection() { directionModifier = new Coords(0 - directionModifier.x, 0 - directionModifier.y); }
//get a modifier that we can add to a coordinate to get a new coordinate in the correct direction private void findDirection() { directionModifier = new Coords((pastHits[pastHits.Count - 1].x - pastHits[pastHits.Count - 2].x), (pastHits[pastHits.Count - 1].y - pastHits[pastHits.Count - 2].y)); }
// used to subract a sunk ship amount of coordinates from past hits in the case that we tagged two ships private void TrimHits(int size) { //int count = pastHits.Count - 1; //int trimTo = count - size; //for (int i = count; i > trimTo; i--) //{ // pastHits.RemoveAt(i); //} // new code to replace old starts here Coords lastHit = pastHits[pastHits.Count - 1]; // if we are horizontal if ((lastHit.y - pastHits[0].y) == 0) { if ((lastHit.x - pastHits[0].x) < 0) { // O(n^m) which is worrisome, possible candidate for refactor, might not matter with the max of m being 5 though for (int i = 0; i < size; i++) { foreach (Coords coord in pastHits) { if ((coord.x - i) == lastHit.x) { pastHits.Remove(coord); break; } } } } else { for (int i = 0; i < size; i++) { foreach (Coords coord in pastHits) { if ((coord.x + i) == lastHit.x) { pastHits.Remove(coord); break; } } } } } // otherwise we are vertical orientation else { if ((lastHit.y - pastHits[0].y) < 0) { for (int i = 0; i < size; i++) { foreach (Coords coord in pastHits) { if ((coord.y - i) == lastHit.y) { pastHits.Remove(coord); break; } } } } else { for (int i = 0; i < size; i++) { foreach (Coords coord in pastHits) { if ((coord.y + i) == lastHit.y) { pastHits.Remove(coord); break; } } } } } }
public Coords TakeTurn() { Coords guess = new Coords(0, 0); Coords lastGuess; bool valid = false; /* Handle the AI behavior based on state. The states are as follows: * 1 - random guesses to find a hit. * 2 - got a hit but searching for a path to keep getting hits * 3 - following the path that is found until sinking a ship or getting a miss * 4 - path reached end, now going the other direction to score a sink * 5 - continuing the direction following the reversal, similar to state 3 * 6 - go to a multihit location and start going, similar to state 4 * 7 - continue the direction, similar to state 3 and 5 * 8 - When we have already flipped during the multihit stage, look out for multihits within multihits * 9 - contrinuing along post reverse looking out for multihits within multihits * */ while (!valid) { switch (AIstate) { case 1: guess = RandomGuess(); // validation built into the function valid = true; break; case 2: int trialHit = randomizer.Next(hitArea.Count); guess = hitArea[trialHit]; // validation built into the function valid = true; pastGuesses.Add(guess); hitArea.RemoveAt(trialHit); break; case 3: lastGuess = pastGuesses[pastGuesses.Count - 1]; guess = new Coords((lastGuess.x + directionModifier.x), (lastGuess.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 4: guess = new Coords((hitSpot.x + directionModifier.x), (hitSpot.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 5: lastGuess = pastGuesses[pastGuesses.Count - 1]; guess = new Coords((lastGuess.x + directionModifier.x), (lastGuess.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 6: guess = new Coords((hitSpot.x + directionModifier.x), (hitSpot.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 7: lastGuess = pastGuesses[pastGuesses.Count - 1]; guess = new Coords((lastGuess.x + directionModifier.x), (lastGuess.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 8: guess = new Coords((hitSpot.x + directionModifier.x), (hitSpot.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; case 9: lastGuess = pastGuesses[pastGuesses.Count - 1]; guess = new Coords((lastGuess.x + directionModifier.x), (lastGuess.y + directionModifier.y)); if (ValidateGuess(guess)) { pastGuesses.Add(guess); valid = true; } else { Report(0); } break; default: valid = true; guess = RandomGuess(); break; } } return(guess); }
// takes the result of the guess and sets state accordingly public void Report(int status) { // this controls the logic in case of a miss if (status == 0) { // this gets the last guess so we can use as a reference Coords guess = pastGuesses[pastGuesses.Count - 1]; switch (AIstate) { case 1: break; case 2: if (hitArea.Count == 0) { AIstate = 1; } break; case 3: reverseDirection(); AIstate = 4; break; case 4: ConvertToMultiHit(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; break; case 5: ConvertToMultiHit(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; break; case 6: reverseDirection(); AIstate = 8; break; case 7: reverseDirection(); AIstate = 8; break; case 8: SaveMultiHit(); break; case 9: SaveMultiHit(); break; default: AIstate = 1; break; } } // this controls the logic if a hit is scored else if (status == 1) { // this gets the last guess so we can use as a reference Coords guess = pastGuesses[pastGuesses.Count - 1]; switch (AIstate) { case 1: hitSpot = guess; createHitArea(guess); pastHits.Add(guess); AIstate = 2; break; case 2: pastHits.Add(guess); findDirection(); AIstate = 3; break; case 3: pastHits.Add(guess); break; case 4: pastHits.Add(guess); AIstate = 5; break; case 5: pastHits.Add(guess); break; case 6: pastHits.Add(guess); AIstate = 7; break; case 7: pastHits.Add(guess); break; case 8: pastHits.Add(guess); AIstate = 9; break; case 9: pastHits.Add(guess); break; default: AIstate = 1; break; } } // this controls the logic if a ship is sunk else if (status > 1) { int size; Coords guess = pastGuesses[pastGuesses.Count - 1]; pastHits.Add(guess); switch (status) { case 2: size = 2; break; case 3: size = 3; break; case 4: size = 3; break; case 5: size = 4; break; case 6: size = 5; break; default: //something has gone horribly wrong size = 0; break; } switch (AIstate) { case 1: resetState(); AIstate = 1; break; case 2: resetState(); AIstate = 1; break; case 3: if (pastHits.Count == size) { resetState(); AIstate = 1; } else { TrimHits(size); reverseDirection(); AIstate = 4; } break; case 4: if (pastHits.Count == size) { resetState(); AIstate = 1; } else { TrimHits(size); ConvertToMultiHit(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; break; } break; case 5: if (pastHits.Count == size) { resetState(); AIstate = 1; } else { TrimHits(size); ConvertToMultiHit(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; break; } break; case 6: if (pastHits.Count == size) { pastHits.Clear(); if (multiHit.Count == 0) { resetState(); AIstate = 1; } else { hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); } } else { TrimHits(size); reverseDirection(); AIstate = 6; } break; case 7: if (pastHits.Count == size) { pastHits.Clear(); if (multiHit.Count == 0) { resetState(); AIstate = 1; } else { hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); AIstate = 6; } } else { TrimHits(size); reverseDirection(); AIstate = 6; } break; case 8: if (pastHits.Count == size) { pastHits.Clear(); if (multiHit.Count == 0) { resetState(); AIstate = 1; } else { hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); } } else { TrimHits(size); SaveMultiHit(); // get a new multihit going if (multiHit.Count == 0) { multiHit = chainedMultiHits.Dequeue(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; } // save for later else { pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); AIstate = 6; } } break; case 9: if (pastHits.Count == size) { pastHits.Clear(); if (multiHit.Count == 0) { resetState(); AIstate = 1; } else { hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); AIstate = 6; } } else { TrimHits(size); SaveMultiHit(); // get a new multihit going if (multiHit.Count == 0) { multiHit = chainedMultiHits.Dequeue(); pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); findDirectionMulti(hitSpot); AIstate = 6; } // save for later else { pastHits.Clear(); hitSpot = multiHit.Dequeue(); pastHits.Add(hitSpot); AIstate = 6; } } break; default: resetState(); AIstate = 1; break; } } }