• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

samsmithnz / PuzzleSolver / 4384022237

pending completion
4384022237

Pull #52

github

GitHub
Merge c981e8eec into a11a49d0f
Pull Request #52: Resolving paths from crossing

926 of 998 branches covered (92.79%)

Branch coverage included in aggregate %.

70 of 70 new or added lines in 4 files covered. (100.0%)

1619 of 1668 relevant lines covered (97.06%)

1104773.11 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.3
/src/PuzzleSolver/Board.cs
1
using PuzzleSolver.Images;
2
using PuzzleSolver.Map;
3
using PuzzleSolver.MultipleRobots;
4
using PuzzleSolver.Processing;
5
using SixLabors.ImageSharp.PixelFormats;
6
using System.Collections.Generic;
7
using System.Linq;
8
using System.Numerics;
9

10
namespace PuzzleSolver
11
{
12
    public class Board
13
    {
14
        public string[,] Map { get; set; }
177✔
15

16
        //Color Palette
17
        public List<Rgb24> ColorPalette { get; set; }
16✔
18

19
        //Pieces
20
        public Vector2 UnsortedPiecesLocation { get; set; }
10✔
21
        public Queue<Piece> UnsortedPieces { get; set; }
306✔
22
        public List<Piece> SortedPieces { get; set; }
75✔
23
        public List<SortedDropZone> SortedDropZones { get; set; }
305✔
24

25
        //Characters
26
        public List<Robot> Robots { get; set; }
240✔
27

28
        //Constructor
29
        public Board(string[,] map,
8✔
30
            Vector2 unsortedPiecesLocation,
8✔
31
            List<Rgb24> colorPalette,
8✔
32
            List<Piece> unsortedPieceList,
8✔
33
            List<SortedDropZone> sortedDropZones,
8✔
34
            List<Robot> robots)
8✔
35
        {
8✔
36
            Map = map;
8✔
37
            UnsortedPiecesLocation = unsortedPiecesLocation;
8✔
38
            ColorPalette = colorPalette;
8✔
39
            UnsortedPieces = new Queue<Piece>();
8✔
40
            ImageColorGroups imageProcessing = new ImageColorGroups(ColorPalette);
8✔
41
            for (int i = 0; i < unsortedPieceList.Count; i++)
144✔
42
            {
64✔
43
                Piece piece = unsortedPieceList[i];
64✔
44
                piece.ImageStats = imageProcessing.ProcessStatsForImage(null, piece.Image);
64✔
45
                UnsortedPieces.Enqueue(piece);
64✔
46
            }
64✔
47
            SortedDropZones = sortedDropZones;
8✔
48
            SortedPieces = new List<Piece>();
8✔
49
            Robots = robots;
8✔
50
        }
8✔
51

52
        public TimeLine RunRobots2()
53
        {
×
54
            TimeLine timeLine = new TimeLine();
×
55
            //Create a dictonary to track robot turn progress over time
56
            Dictionary<int, int> robotProgress = new Dictionary<int, int>();
×
57
            foreach (Robot robot in Robots)
×
58
            {
×
59
                robotProgress.Add(robot.RobotId, 0);
×
60
            }
×
61

62
            //Need to loop through all unsorted pieces until they are sorted
63
            while (UnsortedPieces.Count > 0)
×
64
            {
×
65
                //Sort the progress list to find the robot with the least number of turns - this is the robot who should pick up next
66
                List<KeyValuePair<int, int>> orderedRobotProgress = robotProgress.OrderBy(x => x.Value).ToList();
×
67
                //For each robot
68
                foreach (Robot robot in Robots)
×
69
                {
×
70
                    switch (robot.RobotStatus)
×
71
                    {
72
                        case RobotStatus.RobotStatusEnum.LookingForJob:
73
                            //If there are unsorted pieces, move to pickup
74
                            break;
×
75
                        case RobotStatus.RobotStatusEnum.MovingToPickup:
76
                            //If we are at the pickup location, and there are pieces, pick up the piece
77
                            break;
×
78
                        case RobotStatus.RobotStatusEnum.PickingUp:
79
                            //If the piece is picked up, move to dropoff
80
                            break;
×
81
                        case RobotStatus.RobotStatusEnum.MovingToDropoff:
82
                            //If we are at the dropoff location, drop off the piece
83
                            break;
×
84
                        case RobotStatus.RobotStatusEnum.DroppingOff:
85
                            //If the piece is dropped off, move to pickup
86
                            break;
×
87
                    }
88
                }
×
89
            }
×
90

91
            return timeLine;
×
92
        }
×
93

94
        public TimeLine RunRobots()
95
        {
6✔
96
            TimeLine timeline = new TimeLine();
6✔
97

98
            //Create a dictonary to track robot turn progress over time
99
            Dictionary<int, int> robotProgress = new Dictionary<int, int>();
6✔
100
            foreach (Robot robot in Robots)
34✔
101
            {
8✔
102
                robotProgress.Add(robot.RobotId, 0);
8✔
103
            }
8✔
104

105
            //Need to loop through all unsorted pieces until they are sorted
106
            while (UnsortedPieces.Count > 0)
111✔
107
            {
105✔
108
                //Sort the progress list to find the robot with the least number of turns - this is the robot who should pick up next
109
                List<KeyValuePair<int, int>> orderedRobotProgress = robotProgress.OrderBy(x => x.Value).ToList();
247✔
110
                //For each robot
111
                foreach (Robot robot in Robots)
458✔
112
                {
124✔
113
                    //Find the robot with the least progress, and then break
114
                    if (robot.Piece == null && orderedRobotProgress[0].Key == robot.RobotId)
124!
115
                    {
105✔
116
                        RobotAction robotAction = new RobotAction();
105✔
117
                        Piece piece = null;
105✔
118
                        //See if the robot needs to move to the pickup zone
119
                        if (robot.Location != robot.PickupLocation)
105✔
120
                        {
49✔
121
                            //Move the robot to the pickup zone - By doing this first we ensure we don't pick up a piece until we are there.
122
                            Vector2 currentRobotLocation = robot.Location;
49✔
123
                            Vector2 pickupLocation = robot.PickupLocation;
49✔
124

125
                            // Move to unsorted pile
126
                            robotAction.RobotPickupStartingLocation = currentRobotLocation;
49✔
127
                            if (currentRobotLocation != pickupLocation)
49✔
128
                            {
49✔
129
                                PathFindingResult pathFindingResultForPickup = PathFinding.FindPath(Map, currentRobotLocation, pickupLocation, Robots);
49✔
130
                                if (pathFindingResultForPickup != null && pathFindingResultForPickup.Path.Any())
49!
131
                                {
49✔
132
                                    //Move robot
133
                                    robotAction.PathToPickup = pathFindingResultForPickup;
49✔
134
                                    currentRobotLocation = pathFindingResultForPickup.Path.Last();
49✔
135
                                }
49✔
136
                            }
49✔
137
                            robotAction.RobotPickupEndingLocation = currentRobotLocation;
49✔
138
                            robot.Location = currentRobotLocation;
49✔
139
                        }
49✔
140
                        //Else do the pickup, dropoff and delivery
141
                        else if (robot.Piece == null && UnsortedPieces.Count > 0)
56!
142
                        {
56✔
143
                            piece = UnsortedPieces.Dequeue();
56✔
144
                            robotAction = GetRobotAction(robot, piece);
56✔
145
                        }
56✔
146
                        ////Robot is holding the piece
147
                        //else if (robot.Piece != null)
148
                        //{
149
                        //    robotAction = GetRobotAction(robot, robot.Piece);
150
                        //    if (robot.Location != robotAction.RobotDropoffEndingLocation)
151
                        //    {
152
                        //        //need to find a dropoff path
153
                        //    }
154
                        //    else
155
                        //    {
156
                        //        //is at the dropoff point
157
                        //    }
158
                        //}
159

160
                        //process the robot action
161
                        if (robotAction != null)
105✔
162
                        {
105✔
163
                            int turn = robotProgress[robot.RobotId];
105✔
164
                            int turnsNeeded = 0;
105✔
165

166
                            //move to pickup
167
                            if (robotAction.PathToPickup != null)
105✔
168
                            {
49✔
169
                                turnsNeeded += robotAction.PathToPickup.Path.Count;
49✔
170
                            }
49✔
171
                            //pickup piece
172
                            if (robotAction.PickupAction != null)
105✔
173
                            {
56✔
174
                                turnsNeeded++;
56✔
175
                            }
56✔
176
                            //move to drop off
177
                            if (robotAction.PathToDropoff != null)
105✔
178
                            {
56✔
179
                                turnsNeeded += robotAction.PathToDropoff.Path.Count;
56✔
180
                            }
56✔
181
                            //drop off piece
182
                            if (robotAction.DropoffAction != null)
105✔
183
                            {
56✔
184
                                turnsNeeded++;
56✔
185
                            }
56✔
186
                            //Initialize the turns needed for this robot to complete it's turn
187
                            for (int j = turn; j < turn + turnsNeeded; j++)
1,002✔
188
                            {
396✔
189
                                if (timeline.Turns.Any(t => t.TurnNumber == j + 1) == false)
12,004✔
190
                                {
321✔
191
                                    timeline.Turns.Add(new Turn(j + 1));
321✔
192
                                }
321✔
193
                            }
396✔
194

195
                            //Now populate the turns with the pickup path
196
                            int pickupCounter = 0;
105✔
197
                            if (robotAction.PathToPickup != null &&
105✔
198
                                robotAction.PathToPickup.Path != null &&
105✔
199
                                robotAction.PathToPickup.Path.Count > 0)
105✔
200
                            {
49✔
201
                                pickupCounter++;
49✔
202
                                timeline.Turns[turn].RobotActions.Add(new RobotTurnAction(robot.RobotId, null)
49✔
203
                                {
49✔
204
                                    Movement = new List<Vector2>() { robotAction.RobotPickupStartingLocation, robotAction.PathToPickup.Path[0] }
49✔
205
                                });
49✔
206
                                for (int j = 1; j <= robotAction.PathToPickup.Path.Count - 1; j++)
264✔
207
                                {
83✔
208
                                    pickupCounter++;
83✔
209
                                    timeline.Turns[turn + j].RobotActions.Add(new RobotTurnAction(robot.RobotId, null)
83✔
210
                                    {
83✔
211
                                        Movement = new List<Vector2>() { robotAction.PathToPickup.Path[j - 1], robotAction.PathToPickup.Path[j] }
83✔
212
                                    });
83✔
213
                                }
83✔
214
                            }
49✔
215

216
                            if (robotAction.PickupAction != null)
105✔
217
                            {
56✔
218
                                timeline.Turns[pickupCounter + turn].RobotActions.Add(new RobotTurnAction(robot.RobotId, piece.Id)
56✔
219
                                {
56✔
220
                                    PickupAction = robotAction.PickupAction
56✔
221
                                });
56✔
222
                                pickupCounter++;
56✔
223
                                robot.Location = robotAction.RobotPickupEndingLocation;
56✔
224
                            }
56✔
225

226
                            //Now populate the turns with the dropoff path
227
                            int dropoffCounter = 0;
105✔
228
                            if (robotAction.PathToDropoff != null &&
105✔
229
                                robotAction.PathToDropoff.Path != null &&
105✔
230
                                robotAction.PathToDropoff.Path.Count > 0)
105✔
231
                            {
55✔
232
                                dropoffCounter++;
55✔
233
                                timeline.Turns[pickupCounter + turn].RobotActions.Add(new RobotTurnAction(robot.RobotId, piece.Id)
55✔
234
                                {
55✔
235
                                    Movement = new List<Vector2>() { robotAction.RobotDropoffStartingLocation, robotAction.PathToDropoff.Path[0] }
55✔
236
                                });
55✔
237
                                for (int j = 1; j <= robotAction.PathToDropoff.Path.Count - 1; j++)
304✔
238
                                {
97✔
239
                                    dropoffCounter++;
97✔
240
                                    timeline.Turns[pickupCounter + turn + j].RobotActions.Add(new RobotTurnAction(robot.RobotId, piece.Id)
97✔
241
                                    {
97✔
242
                                        Movement = new List<Vector2>() { robotAction.PathToDropoff.Path[j - 1], robotAction.PathToDropoff.Path[j] }
97✔
243
                                    });
97✔
244
                                }
97✔
245
                            }
55✔
246

247
                            if (robotAction.DropoffAction != null)
105✔
248
                            {
56✔
249
                                robotAction.DropoffAction.DestinationPieceCount = GetPieceCount(robotAction.DropoffAction.Location);
56✔
250
                                timeline.Turns[pickupCounter + dropoffCounter + turn].RobotActions.Add(new RobotTurnAction(robot.RobotId, piece.Id)
56✔
251
                                {
56✔
252
                                    DropoffAction = robotAction.DropoffAction
56✔
253
                                });
56✔
254
                                dropoffCounter++;
56✔
255
                                robot.Location = robotAction.RobotDropoffEndingLocation;
56✔
256
                            }
56✔
257
                            robotProgress[robot.RobotId] += pickupCounter + dropoffCounter;
105✔
258
                        }
105✔
259
                        break;
105✔
260
                    }
261
                }
19✔
262
            }
105✔
263

264
            //Move the robots back to the start location
265
            foreach (Robot robot in Robots)
34✔
266
            {
8✔
267
                if (robot.Location != robot.PickupLocation)
8✔
268
                {
6✔
269
                    RobotAction robotAction = new RobotAction();
6✔
270
                    //Move the robot to the pickup zone - By doing this first we ensure we don't pick up a piece until we are there.
271
                    Vector2 currentRobotLocation = robot.Location;
6✔
272
                    Vector2 pickupLocation = robot.PickupLocation;
6✔
273

274
                    // Move to unsorted pile
275
                    robotAction.RobotPickupStartingLocation = currentRobotLocation;
6✔
276
                    if (currentRobotLocation != pickupLocation)
6✔
277
                    {
6✔
278
                        PathFindingResult pathFindingResultForPickup = PathFinding.FindPath(Map, currentRobotLocation, pickupLocation, Robots);
6✔
279
                        if (pathFindingResultForPickup != null && pathFindingResultForPickup.Path.Any())
6!
280
                        {
6✔
281
                            //Move robot
282
                            robotAction.PathToPickup = pathFindingResultForPickup;
6✔
283
                            currentRobotLocation = pathFindingResultForPickup.Path.Last();
6✔
284
                        }
6✔
285
                    }
6✔
286
                    robotAction.RobotPickupEndingLocation = currentRobotLocation;
6✔
287
                    robot.Location = currentRobotLocation;
6✔
288

289
                    //process the robot action
290
                    if (robotAction != null)
6✔
291
                    {
6✔
292
                        int turn = robotProgress[robot.RobotId];
6✔
293
                        int turnsNeeded = 0;
6✔
294

295
                        //move to pickup
296
                        if (robotAction.PathToPickup != null)
6✔
297
                        {
6✔
298
                            turnsNeeded += robotAction.PathToPickup.Path.Count;
6✔
299
                        }
6✔
300
                        //Initialize the turns needed for this robot to complete it's turn
301
                        for (int j = turn; j < turn + turnsNeeded; j++)
52✔
302
                        {
20✔
303
                            if (timeline.Turns.Any(t => t.TurnNumber == j + 1) == false)
1,074✔
304
                            {
17✔
305
                                timeline.Turns.Add(new Turn(j + 1));
17✔
306
                            }
17✔
307
                        }
20✔
308

309
                        //Now populate the turns with the pickup path
310
                        int pickupCounter = 0;
6✔
311
                        if (robotAction.PathToPickup != null &&
6!
312
                            robotAction.PathToPickup.Path != null &&
6✔
313
                            robotAction.PathToPickup.Path.Count > 0)
6✔
314
                        {
6✔
315
                            pickupCounter++;
6✔
316
                            timeline.Turns[turn].RobotActions.Add(new RobotTurnAction(robot.RobotId, null)
6✔
317
                            {
6✔
318
                                Movement = new List<Vector2>() { robotAction.RobotPickupStartingLocation, robotAction.PathToPickup.Path[0] }
6✔
319
                            });
6✔
320
                            for (int j = 1; j <= robotAction.PathToPickup.Path.Count - 1; j++)
40✔
321
                            {
14✔
322
                                pickupCounter++;
14✔
323
                                timeline.Turns[turn + j].RobotActions.Add(new RobotTurnAction(robot.RobotId, null)
14✔
324
                                {
14✔
325
                                    Movement = new List<Vector2>() { robotAction.PathToPickup.Path[j - 1], robotAction.PathToPickup.Path[j] }
14✔
326
                                });
14✔
327
                            }
14✔
328
                        }
6✔
329
                        robotProgress[robot.RobotId] += pickupCounter;
6✔
330
                    }
6✔
331
                }
6✔
332
            }
8✔
333

334
            return timeline;
6✔
335
        }
6✔
336

337
        public int GetPieceCount(Vector2 dropOfflocation)
338
        {
114✔
339
            int pieceCount = 0;
114✔
340
            foreach (SortedDropZone sortedDropZone in SortedDropZones)
1,084✔
341
            {
428✔
342
                if (sortedDropZone.Location == dropOfflocation)
428✔
343
                {
114✔
344
                    pieceCount = sortedDropZone.Count;
114✔
345
                    break;
114✔
346
                }
347
            }
314✔
348
            return pieceCount;
114✔
349
        }
114✔
350

351
        private Vector2? GetAdjacentLocation(Vector2 destinationLocation, string[,] map, List<SortedDropZone> sortedDropZones)
352
        {
56✔
353
            Vector2? adjacentLocation = null;
56✔
354
            if (destinationLocation != null)
56✔
355
            {
56✔
356
                if (destinationLocation.X == 0)
56✔
357
                {
43✔
358
                    //it's a right location drop-off
359
                    adjacentLocation = new Vector2((int)destinationLocation.X + 1, (int)destinationLocation.Y);
43✔
360
                }
43✔
361
                else if (destinationLocation.X == map.GetUpperBound(0))
13✔
362
                {
1✔
363
                    //it's a left location drop-off
364
                    adjacentLocation = new Vector2((int)destinationLocation.X - 1, (int)destinationLocation.Y);
1✔
365
                }
1✔
366
                else if (destinationLocation.Y == 0)
12✔
367
                {
5✔
368
                    //it's a top location drop-off
369
                    adjacentLocation = new Vector2((int)destinationLocation.X, (int)destinationLocation.Y + 1);
5✔
370
                }
5✔
371
                else if (destinationLocation.Y == map.GetUpperBound(1))
7✔
372
                {
7✔
373
                    //it's a bottom location drop-off
374
                    adjacentLocation = new Vector2((int)destinationLocation.X, (int)destinationLocation.Y - 1);
7✔
375
                }
7✔
376
            }
56✔
377
            return adjacentLocation;
56✔
378
        }
56✔
379

380
        private RobotAction GetRobotAction(Robot robot, Piece piece)
381
        {
56✔
382
            RobotAction robotAction = new RobotAction();
56✔
383

384
            Vector2 currentRobotLocation = robot.Location;
56✔
385
            Vector2 pickupLocation = robot.PickupLocation;
56✔
386

387
            // Move to unsorted pile
388
            robotAction.RobotPickupStartingLocation = currentRobotLocation;
56✔
389
            if (currentRobotLocation != pickupLocation)
56!
390
            {
×
391
                PathFindingResult pathFindingResultForPickup = PathFinding.FindPath(Map, currentRobotLocation, pickupLocation, Robots);
×
392
                if (pathFindingResultForPickup != null && pathFindingResultForPickup.Path.Any())
×
393
                {
×
394
                    //Move robot
395
                    robotAction.PathToPickup = pathFindingResultForPickup;
×
396
                    currentRobotLocation = pathFindingResultForPickup.Path.Last();
×
397
                }
×
398
            }
×
399
            robotAction.RobotPickupEndingLocation = currentRobotLocation;
56✔
400

401
            // Pickup an unsorted piece from the unsorted pile
402
            if (robot.Piece != null)
56!
403
            {
×
404
                throw new System.Exception("Piece " + robot.Piece.Id + " was not delivered");
×
405
            }
406
            robot.Piece = piece;
56✔
407
            robotAction.PieceId = piece.Id;
56✔
408
            robotAction.PickupAction = new ObjectInteraction()
56✔
409
            {
56✔
410
                Location = piece.Location
56✔
411
            };
56✔
412

413
            // Process the unsorted piece to work out where it goes
414
            Vector2? destinationLocation = null;
56✔
415
            foreach (SortedDropZone sortedDropZone in SortedDropZones)
538✔
416
            {
213✔
417
                if (sortedDropZone.Color == robot.Piece.ImageStats.TopColorGroupColor)
213✔
418
                {
56✔
419
                    destinationLocation = sortedDropZone.Location;
56✔
420
                    break;
56✔
421
                }
422
            }
157✔
423
            if (destinationLocation == null)
56!
424
            {
×
425
                throw new System.Exception("Destination not found for piece " + piece.Id);
×
426
            }
427

428
            //Get the best adjacent location to the destination
429
            Vector2? pathDestinationLocation = destinationLocation;
56✔
430
            if (destinationLocation != null)
56✔
431
            {
56✔
432
                Vector2? adjacentLocation = GetAdjacentLocation((Vector2)destinationLocation, Map, SortedDropZones);
56✔
433
                if (adjacentLocation != null)
56✔
434
                {
56✔
435
                    pathDestinationLocation = (Vector2)adjacentLocation;
56✔
436
                }
56✔
437
            }
56✔
438

439
            // Move the sorted piece to the correct pile
440
            robotAction.RobotDropoffStartingLocation = currentRobotLocation;
56✔
441
            if (destinationLocation != null && pathDestinationLocation != null)
56!
442
            {
56✔
443
                //now find the path
444
                PathFindingResult pathFindingResultForDropoff = PathFinding.FindPath(Map, currentRobotLocation, (Vector2)pathDestinationLocation, Robots);
56✔
445
                if (pathFindingResultForDropoff != null && pathFindingResultForDropoff.Path.Count >= 0)
56!
446
                {
56✔
447
                    //Move robot
448
                    robotAction.PathToDropoff = pathFindingResultForDropoff;
56✔
449
                    robotAction.DropoffAction = new ObjectInteraction()
56✔
450
                    {
56✔
451
                        Location = (Vector2)destinationLocation
56✔
452
                    };
56✔
453
                    //Move the piece from the robot to the sorted pile
454
                    robot.Piece.Location = robotAction.DropoffAction.Location;
56✔
455
                    SortedPieces.Add(robot.Piece);
56✔
456
                    foreach (SortedDropZone sortedDropZone in SortedDropZones)
538✔
457
                    {
213✔
458
                        if (sortedDropZone.Location == destinationLocation)
213!
459
                        {
56✔
460
                            sortedDropZone.Count++;
56✔
461
                            break;
56✔
462
                        }
463
                    }
157✔
464
                    robot.Piece = null;
56✔
465
                    robotAction.DropoffPieceCount = GetPieceCount(robotAction.DropoffAction.Location);
56✔
466
                    if (pathFindingResultForDropoff.Path.Count > 0)
56✔
467
                    {
55✔
468
                        currentRobotLocation = pathFindingResultForDropoff.Path.Last();
55✔
469
                    }
55✔
470
                }
56✔
471
            }
56✔
472
            robotAction.RobotDropoffEndingLocation = currentRobotLocation;
56✔
473

474
            return robotAction;
56✔
475
        }
56✔
476
    }
477
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc