-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGame.cs
296 lines (289 loc) · 15.8 KB
/
Game.cs
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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace ZooManager
{
static class Game
{
//create a occupants list, use Occupant here because it can check tpye by "is" if there is any new code needs this function
public static readonly Occupant[] occupants = { new Raptor("raptor"), new Chick("chick"), new Cat("cat"), new Mouse("mouse"), new Alien("alien"), };
//2d list for create a zone, will start with y, read from Interaction
static List<List<Zone>> animalZones = Interaction.animalZones;
/*
* function to reduce the repeating codes
* call: none
* called by: Game
* parameter: int - location, int - location, int - distance of the direction
* return: Dictionary<Direction, int> - key is direction, value is coordinate (x, y)
*/
static Dictionary<Direction, int[]> DirCoordinates(int x, int y, int distance)
{
//create the dictionary for the new coordinates of 4 the directions
Dictionary<Direction, int[]> dirCoordinates = new Dictionary<Direction, int[]>()
{
//key: directions, value: coordinates after add distance in the direction
{ Direction.up, new int[] {x, y-distance} },
{ Direction.right, new int[] {x+distance, y} },
{ Direction.down, new int[] {x, y+distance} },
{ Direction.left, new int[] {x-distance, y} },
};
//returb the dictionary
return dirCoordinates;
}
/*
* seek targets in 4 directions distance
* call: Interaction - numCellsY, numCellsX
* called by: Occupant, Game
* parameter: int - location, string[] - targets(use array because the length will not change), int - distance to seek, bool - true is not fly
* return: Dictionary<Direction, int> - key is direction, value is target's distance, List<Direction> - closest/farest directions with target
*/
//feature f, i
static public (Dictionary<Direction, int>, List<Direction>) Seek(int x, int y, string[] targets, int distance, bool nofly=true)
{
//store 4 directions and its distance to target, default distance is 0 (no target==0)
//feature f, i
Dictionary<Direction, int> directionInfo = new Dictionary<Direction, int>()
{
{Direction.up, 0},
{Direction.right, 0},
{Direction.down, 0},
{Direction.left, 0},
};
//list to store closest/farest directions with target
List<Direction> targetDirections = new List<Direction>();
//int to save the closest/farest directions with target
int targetDistance = 10;
//seeking empty cells
//feature i
if (targets==null)
{
//check the farest empty cell, so the distance should be higher in each loop
//it is needed this because the direction in distance might have no occupant
for (int i = 1; i <= distance; i++)
{
//get the coordinates in current distance from the current location
//key is direction, value is axis
Dictionary<Direction, int[]> dirCoordinates = DirCoordinates(x, y, i);
//save the farest empty coordinates of all the direction
foreach (var coordinate in dirCoordinates)
{
//if the direction is not edge
if (coordinate.Value[1] >= 0 && coordinate.Value[0] < Interaction.numCellsX && coordinate.Value[1] < Interaction.numCellsY && coordinate.Value[0] >= 0)
{
//the cell is empty
if (animalZones[coordinate.Value[1]][coordinate.Value[0]].occupant == null)
{
//save 'the distance to the empty' of the direction
//feature i (found empty, >0)
directionInfo[coordinate.Key] = i;
}
}
}
}
//if not flying
if (nofly)
{
//check the closest cell that is occupied, so the distance should be lower in each loop
//this will cover the previous data if matched the conditions
for (int i = distance; i > 0; i--)
{
//get the coordinates in current distance from the current location
//key is direction, value is axis
Dictionary<Direction, int[]> dirCoordinates = DirCoordinates(x, y, i);
//save the closest empty coordinates of all the direction
foreach (var coordinate in dirCoordinates)
{
//if the direction is not edge
if (coordinate.Value[1] >= 0 && coordinate.Value[0] < Interaction.numCellsX && coordinate.Value[1] < Interaction.numCellsY && coordinate.Value[0] >= 0)
{
//check if something on the cells in the direction by lower distance
if (animalZones[coordinate.Value[1]][coordinate.Value[0]].occupant != null)
{
//the closer occupant is at current distance, the empty cell is at previous cell
//so the distance-1 will be the closest empty cell with no occupant in between
directionInfo[coordinate.Key] = i - 1;
}
}
}
}
}
//the function below DirCoordinates can't apply DirCoordinates, because break will break the wrong loop
//up
//if same direction is empty then keep looking next empty in distance
//feature f (distance)
/*for (int i=1; i<=distance; i++)
{
//if up is not edge and up have animal
if (y - i >= 0 && animalZones[y - i][x].occupant != null)
{
//this direction is stop here, so break and seek other directions
break;
}
//if up is not edge and up is empty
else if (y - i >= 0 && animalZones[y - i][x].occupant == null)
{
//add 1 to the distance to the empty of direction up
//feature i (found empty, >0)
directionInfo[Direction.up]++;
}
}
//right......*/
//use for finding farest direction in distance
targetDistance = 0;
//check saved distance in diff direction to find the farest distance in direction
foreach (var target in directionInfo)
{
//if the direction have empty cell and is the distance bigger than current targetDistance, save the new bigger value
if (target.Value > 0 && target.Value > targetDistance) targetDistance = target.Value;
}
}
//if taget is not empty cell
else if (targets != null)
{
//check from farest to closest to find the closest target
for (int i = distance; i >= 1; i--)
{
//get the coordinates in current distance from the current location
//key is direction, value is axis
Dictionary<Direction, int[]> dirCoordinates = DirCoordinates(x, y, i);
//check different targets
for (int j = 0; j < targets.Length; j++)
{
//check all the directions in dirCoordinates
foreach (var coordinate in dirCoordinates)
{
//declare values to make the codes readable
int newY = coordinate.Value[1];
int newX = coordinate.Value[0];
Direction direction = coordinate.Key;
//if the direction is not edge and the direction is one of targets
//animalZones[newY][newX].occupant != null is needed to check animalZones[newY][newX].occupant.name
if (newY >= 0 && newX < Interaction.numCellsX && newY < Interaction.numCellsY && newX >= 0
&& animalZones[newY][newX].occupant != null && animalZones[newY][newX].occupant.name == targets[j])
{
//save the current distance to the direction
//feature f (return nearest distance)
directionInfo[direction] = i;
}
}
}
}
//use for finding closest direction in distance
targetDistance = 10;
//check saved distance in diff direction to find the closest distance in direction
foreach (var target in directionInfo)
{
//if the direction have empty cell and is the distance smaller than current targetDistance, save the new smaller value
if (target.Value > 0 && target.Value < targetDistance) targetDistance = target.Value;
}
}
//check saved distance in diff direction by saved closest/farest distance to find the closest/farest directions
foreach (var target in directionInfo)
{
//if the direction's distance is closest/farest distance, add to list
if (target.Value == targetDistance) targetDirections.Add(target.Key);
}
//return target's direction and distance, closest/farest directions with target
return (directionInfo, targetDirections);
}
/*
* attack animal and take their place
* call: no
* called by: Occupant
* parameter: Occupant - attacker, Direction - location, int - distance to attack
* return: no (void)
*/
static public void Attack(Occupant attacker, Direction d, int distance)
{
Console.WriteLine($"{attacker.name} is attacking {d.ToString()}");
//get the coordinates in distance from the current location
//key is direction, value is axis
Dictionary<Direction, int[]> dirCoordinates = DirCoordinates(attacker.location.x, attacker.location.y, distance);
//declare values to make the codes readable
int newY = dirCoordinates[d][1];
int newX = dirCoordinates[d][0];
//make it moved to avoid move more than once
//feature o
animalZones[attacker.location.y][attacker.location.x].occupant.moved = true;
//the attacker remove from the orig location, should remove first
animalZones[attacker.location.y][attacker.location.x].occupant = null;
//attacker take place of the animal that be attacked
animalZones[newY][newX].occupant = attacker;
}
/*
* move to a empty cell in distance and will not move back
* call: Game - Seek
* called by: Occupant
* parameter: Occupant - mover, int - distance to move, Direction? - the direction to move back
* return: int - remain distance, Direction? - the direction to move back
*/
//feature g, h, i
static public (int, Direction?) Move(Occupant mover, int distance, Direction? origDirection, List<Direction>? predatorDirections, bool nofly=true)
{
//get loacation of mover
int x = mover.location.x;
int y = mover.location.y;
//store the distance that moved in this time
int movedCell = 0;
//get the list of farest directions that can move
//feature g (move to a empty cell), i (target is empty)
(Dictionary<Direction, int> directionInfo, List<Direction> targetDirections) = Game.Seek(x, y, null, distance, nofly);
//avoid the direction that have predator occupied, so that the animanl will not go to the directions and be eaten
if (predatorDirections != null)
{
//check the list in predatorDirections
foreach (Direction direction in predatorDirections)
{
//remove the direction from list in targetDirections
targetDirections.Remove(direction);
}
}
//check the list of directions to avoid move back
//feature h
for (int i = 0; i<targetDirections.Count; i++)
{
//if have moved (origDirection!=null) and origDirection in the list
if (origDirection!=null && targetDirections[i]==origDirection)
{
//remove the direction to move back from the list
targetDirections.RemoveAt(i);
}
}
//if have a cell to move
if (targetDirections.Count > 0)
{
//choose a direction in the list to move radomly
Direction newDir = targetDirections[new Random().Next(0, targetDirections.Count)];
//save the cell that moved according to the distance that return from Seek method
// all the distance in targetDirections are the same
movedCell = directionInfo[targetDirections[0]];
Console.WriteLine($"{mover.name} is moving to {newDir.ToString()} {movedCell} cells");
//get the coordinates in distance from the current location
//key is direction, value is axis
Dictionary<Direction, int[]> dirCoordinates = DirCoordinates(mover.location.x, mover.location.y, movedCell);
//declare values to make the codes readable
int newY = dirCoordinates[newDir][1];
int newX = dirCoordinates[newDir][0];
//make it moved to avoid move more than once
//feature o
animalZones[mover.location.y][mover.location.x].occupant.moved = true;
//mover remove from the orig cell, should remove first
animalZones[mover.location.y][mover.location.x].occupant = null;
//move to the new cell
animalZones[newY][newX].occupant = mover;
//https://stackoverflow.com/questions/4538894/get-index-of-a-key-value-pair-in-a-c-sharp-dictionary-based-on-the-value
//get the index of newDir in dirCoordinates
int dirIndex = dirCoordinates.Keys.ToList().IndexOf(newDir);
//https://learn.microsoft.com/zh-tw/dotnet/api/system.linq.enumerable.elementat?view=net-7.0
//save the move back direction
//the DirCoordinates dict is organized by top, right, down, left, so the opposite direction is in the next two or previous two index
origDirection = dirCoordinates.ElementAt(dirIndex+2 > 3 ? dirIndex-2 : dirIndex+2).Key;
}
//return distance that moved in this time
//feature g
return (movedCell, origDirection);
}
}
}