-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathArtificialIntelligence.cs
More file actions
179 lines (163 loc) · 8.01 KB
/
ArtificialIntelligence.cs
File metadata and controls
179 lines (163 loc) · 8.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Broadsides
{
class ArtificialIntelligence
{
private Coordinate lastShipHitCoordinate;
private Coordinate direction; // Newest Shot which hit a ship - Oldest Shot which hit a ship
/*
DIRECTION:
NO: 00 Y, X
UP: -Y -1, 0
DW: +Y 1, 0
RT: +X 0, 1
LT: -X 0, -1
*/
private bool lastShipHitSunk;
private int streak;
public Coordinate LastShipHitCoordinate
{
get { return this.lastShipHitCoordinate; }
set { this.lastShipHitCoordinate = value; }
}
public Coordinate Direction
{
get { return this.direction; }
set { this.direction = value; }
}
public bool LastShipHitSunk
{
get { return this.lastShipHitSunk; }
set { this.lastShipHitSunk = value; }
}
public int Streak
{
get { return this.streak; }
set { this.streak = value; }
}
public ArtificialIntelligence()
{
lastShipHitSunk = true;
lastShipHitCoordinate = new Coordinate(rnd.Next(0,10), rnd.Next(0, 10));
direction = new Coordinate(0, 0); // Initially no direction
streak = 0;
}
public Random rnd = new Random();
/// <summary>
/// Gets the next planned or random shot for the AI.
/// </summary>
/// <param name="board">Player's board.</param>
/// <returns>Coordinates for the next potential shot</returns>
public Coordinate GetNextShot(Field[][] board)
{
/* Modus Operandi of the AI:
1. Randomly hit ship.
-lastShipHit Set, LastShipSunk False, streak++
-Go through next steps until ship is Sunk or lost.
2. Shoot adjacent squares to find Direction of ship.
-When next part found: lastShipHit set, Direction set, streak++
3. Shoot in the Direction until ship sinks OR...
-lastShipHit set, streak++
4. ..AI wants to shoot a Hit Field OR it points outside the Board. Time to turn back around to get the other half of ship.
-lastShipHit set, Direction Inverted,
*/
Coordinate nextShot = new Coordinate(this.lastShipHitCoordinate.Y, this.lastShipHitCoordinate.X);
if (!this.lastShipHitSunk)
{
// The AI knows of an unsunken ship!
if (this.direction.Y == 0 && this.direction.X == 0)
{
// The AI does Not know the Direction of the Ship, therefore..
// ..It will shoot at a random adjacent field, if it has not already been hit.
// List of Potential Directions: Down, Up, Right, Left
List<Coordinate> potenDirects = new List<Coordinate>
{
new Coordinate(1,0), new Coordinate(-1,0), new Coordinate(0,1), new Coordinate(0,-1)
};
bool goodPotDirect = false;
int index;
while (!goodPotDirect && potenDirects.Count > 0)
{
index = rnd.Next(0, potenDirects.Count);
if(
// If the Potentail Direction points to a field Outside the Board...
this.lastShipHitCoordinate.Y + potenDirects[index].Y < 0 || this.lastShipHitCoordinate.Y + potenDirects[index].Y >= board.Length ||
this.lastShipHitCoordinate.X + potenDirects[index].X < 0 || this.lastShipHitCoordinate.X + potenDirects[index].X >= board.Length ||
// ..or to a Field which has already been hit..
board[this.lastShipHitCoordinate.Y + potenDirects[index].Y][this.lastShipHitCoordinate.X + potenDirects[index].X].IsHit
)
{
// Remove the potential direction from Potential Directions, for it has no potential!
potenDirects.RemoveAt(index);
}
else
{
// This Potential Direction is a Good Potential Direction, and will be shot at!
goodPotDirect = true;
nextShot = nextShot.Add(potenDirects[index]);
}
}
}
else
{
// The AI Knows the Direction of the Ship!
if (
// If the Direction points inside the board or not
nextShot.Y + this.direction.Y >= 0 && nextShot.Y + this.direction.Y < board[0].Length &&
nextShot.X + this.direction.X >= 0 && nextShot.X + this.direction.X < board[0].Length &&
// If the Direction points to a Field which is Hit or not.
!board[nextShot.Y + this.direction.Y][nextShot.X + this.direction.X].IsHit
)
{
// The Direction points to a Field inside the Board which has not been hit
// The field has not been shot, and is inside the board, and should therefore be shot!
nextShot = nextShot.Add(this.direction);
}
else
{
// The Direction points Outside the board or to a Field which has already been Hit, so it is time for a Reversal using Direction and STREAK!
// Reversing Direction first
this.direction.Y = this.direction.Y * -1;
this.direction.X = this.direction.X * -1;
// Applying Streak multiplied with Direction, which lands the shot right on the other half of the ship.
nextShot = nextShot.Add(new Coordinate(this.direction.Y * this.streak, this.direction.X * this.streak));
}
}
}
// One last Contingency Check, just in case that the AI wants to shoot at a Field which has already been Hit or does not exist (Outside the board).
nextShot = NextShotContingency(nextShot, board);
return nextShot;
}
/// <summary>
/// Contingency Check and Apply if the Next Shot is not a sensible one, or it does not know what to shoot at.
/// </summary>
/// <param name="nextShot">AI's suggested next shot.</param>
/// <param name="board">Player's Board, which the AI is about to shoot at.</param>
/// <returns>An approved shot or a randomized shot.</returns>
public Coordinate NextShotContingency(Coordinate nextShot, Field[][] board)
{
if (nextShot.Y < 0 || nextShot.Y >= board.Length || nextShot.X < 0 || nextShot.X >= board[0].Length || board[nextShot.Y][nextShot.X].IsHit)
{
// AI wants to shoot at a field that has already been hit or is located outside of the board.
// THese are not acceptable solutions, therefore AI must forget about the current ship (If any) and return to taking random potshots until it hits a ship.
nextShot.X = rnd.Next(0, 10);
nextShot.Y = rnd.Next(0, 10);
this.ResetTargetting();
}
return nextShot;
}
/// <summary>
/// Resets the AI's targetting, making it forget about any sunk ships or ships it failed to sink.
/// </summary>
public void ResetTargetting()
{
this.streak = 0;
this.direction = new Coordinate(0, 0);
this.lastShipHitSunk = true;
}
}
}