-
Notifications
You must be signed in to change notification settings - Fork 0
/
game.cpp
1444 lines (1333 loc) · 76.6 KB
/
game.cpp
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
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//#define NOMINMAX need to uncomment these if using a Windows computer to debug
#include <iostream>
#include <limits>
#include <random>
#include <string>
#include "game.h"
#include "ship.h"
//#ifdef __unix__
//#include <unistd.h>
//#elif defined _WIN32
//#include <windows.h>
//#define sleep(x) Sleep(1000 * (x))
//#endif
Game::Game() // game constructor
{
makeBoard(); // calls maeBoard function
// row = 11;
// col = 11;
shipAmount = 0; // sets shipAmount to 0 for the start of a board
ai_mode = 0;
player1 = nullptr; // sets the player1 pointer to nullptr
player2 = nullptr; // sets the player2 pointer to nullptr
}
Game::~Game()
{ // game destructor
for (int i = 0; i < row; i++)
{ // runs for each row in the board
delete[] board[i]; // deletes the row at int i
}
delete[] board; // deletes the empty board
delete player1; // deletes the player1 instance of player class
delete player2; // deletes the player2 instance of player class
}
void Game::makeBoard() // game class function 'makeBoard' creates an int 2D array initialized to 0
{
board = new int *[row]; // sets board as a new array of ints
for (int i = 0; i < row; i++) // for each row of the board...
{
board[i] = new int[col]; // set the row of board at int i to have a columb effectively creating a 2d array
}
for (int i = 0; i < row; i++) // for each row of the board...
{
for (int j = 0; j < col; j++) // for each columb of the board...
{
board[i][j] = 0; // initialze value to 0
}
}
labels(); // call the labels function
}
void Game::labels() // game class function 'labels' sets the values on the edges of the board to their coordinate representations
{
board[0][0] = '-'; // this line and the following 10 set the first row of the board to visually represent coordinates
board[0][1] = 'A';
board[0][2] = 'B';
board[0][3] = 'C';
board[0][4] = 'D';
board[0][5] = 'E';
board[0][6] = 'F';
board[0][7] = 'G';
board[0][8] = 'H';
board[0][9] = 'I';
board[0][10] = 'J';
int val = 1; // initializes an int 'val' to 0. only within the scope of labels function
for (int i = 1; i < row; i++) // for each row of the board...
{
board[i][0] = val; // sets the first columb of the board to values 1-10
val++; // increases val by 1 each iteration of the for loop
}
}
void Game::printBoard() // game class function 'printBoard' prints the board to the command line
{
for (int i = 0; i < row; i++) // for each row of the board...
{
for (int j = 0; j < col; j++) // for each columb of the board...
{
if (j == 0 && 0 < i && i < 10) // if the coordinate is on the first columb and on row 1 through 9...
{
std::cout << board[i][j] << " "; // print the board value with correct visual spacing
}
else
{
if ((int)board[i][j] > 10)
{
std::cout << (char)board[i][j] << " ";
if (i == 0 && j == 0) // if '-'
{
std::cout << " ";
}
}
else
{
std::cout << board[i][j] << " ";
}
}
}
std::cout << '\n'; // print new line
}
}
void Game::printScore(Player* thisPlayer, Player* otherPlayer) {
int thisPlayerNumSunk, otherPlayerNumSunk = 0;
std::string thisStatus = "\n\n[SYSTEM] ";
std::string otherStatus = "\n[SYSTEM] ";
int thisSunk = 0;
int otherSunk = 0;
std::string thisShipString = "[SYSTEM] Your ships:";
std::string otherShipString = "[SYSTEM] The enemy's ships:";
for (int i = 0; i < shipAmount; i++) {
std::string thisTemp = " ";
std::string otherTemp = "";
switch (i) {
case 0: thisTemp += "[1x1]: "; break;
case 1: thisTemp += "[1x2]: "; break;
case 2: thisTemp += "[1x3]: "; break;
case 3: thisTemp += "[1x4]: "; break;
case 4: thisTemp += "[1x5]: "; break;
}
otherTemp = thisTemp;
if (thisPlayer->getShip(i)->isSank()) {
thisTemp += "Sunk!";
thisSunk++;
}
else {
thisTemp += std::to_string(thisPlayer->getShip(i)->getLives());
if (thisPlayer->getShip(i)->getLives() == 1) { thisTemp += " life left."; }
else { thisTemp += " lives left."; }
}
if (otherPlayer->getShip(i)->isSank()) {
otherTemp += "Sunk!";
otherSunk++;
}
else {
otherTemp += std::to_string(otherPlayer->getShip(i)->getLives());
if (otherPlayer->getShip(i)->getLives() == 1) { otherTemp += " life left."; }
else { otherTemp += " lives left."; }
}
thisShipString += "\n" + thisTemp;
otherShipString += "\n" + otherTemp;
}
thisStatus += "The enemy sank " + std::to_string(thisSunk) + " of your ships!\n" + thisShipString;
otherStatus += "You sank " + std::to_string(otherSunk) + " of the enemy's ships!\n" + otherShipString;
std::cout << thisStatus << '\n' << '\n';
std::cout << otherStatus << '\n' << '\n';
}
void Game::playSound(std::string State, int Player){
/*
This fuction will play sounds according to the state it's in
miss, hit , and win
*/
if(State == "Win"){ //win sounds
if(ai && Player == 2) //if the AI wins
{
std::cout << "AI wins the game! Better Luck Next Time! \n"; // informs players who won
std::string command = "afplay -v 0.9 Assets/you_lose.mp3";
system(command.c_str());
}
else
{
std::cout << "Player " << Player << " wins the game! \n"; // informs players who won
std::string command = "afplay -v 0.9 Assets/you_won.mp3";
system(command.c_str());
}
}
else if(State == "Miss"){ //miss sound for player
std::string command = "afplay -v 0.5 -t 1.0 -r 2.0 Assets/miss.mp3";
system(command.c_str());
}else if(State == "Hit"){ //hit sound for player
std::string command = "afplay -v 0.5 Assets/hit.mp3";
system(command.c_str());
}
else if(State == "aiMiss"){ //AI miss sound
std::string command = "afplay -v 0.5 -t 1.0 Assets/aiMiss.mp3";
system(command.c_str());
}
else if(State == "aiHit"){//AI hit sound
std::string command = "afplay -v 0.5 Assets/aiHit.mp3";
system(command.c_str());
}
}
void Game::playGame() // game class function 'playGame' controls much of the game logic
{
std::string command = "afplay -v 0.9 -r 1.2 Assets/start.mp3"; //game starting sound **taken from AMOONG US**
system(command.c_str());
int kind_of_game = 0;
std::cout << "Welcome to Battleship!" << std::endl; // prints out welcome message
std::cout << "----------------------" << std::endl; // prints out visual break
std::cout<<"[SYSTEM] - Would you like to play '1 player' or '2 player'?\n[SYSTEM] - Choose 1 or 2: ";
std::cin>>kind_of_game;
std::cout << std::endl;
while (std::cin.fail() || (kind_of_game != 1) && (kind_of_game != 2)) // while input failed
{
std::cin.clear(); // clear the cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
std::cout << "[SYSTEM] - Choose(1 or 2)\n1)1 Player Game\n2)2 Player Game\nselection: "; // ask again for 1 or 2 player game
std::cin >> kind_of_game; // store player entry to kind_of_game
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
if (kind_of_game < 1 || kind_of_game > 2) // if the selection isn't 1 or 2...
{
std::cout << "[SYSTEM] - Mode selection must be either 1 or 2." << std::endl; // tell player selection must be between 1-2
}
if(kind_of_game==1)
{
ai = true;
ai_hardmode_shipIndex = 0; //initiliazes variable for hard AI mode
ai_hardmode_spotIndex = 0; //initiliazes variable for hard AI mode
}
else if(kind_of_game==2)
{
ai = false;
}
do // loop at least once
{
std::cout << "[SYSTEM] - Ship amount: "; // ask for ship amount
std::cin >> shipAmount; // store player entry to shipAmount variable
while (std::cin.fail()) // while input failed
{
std::cin.clear(); // clear the cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
std::cout << "[SYSTEM] - Ship amount (1-5): "; // ask again for the ship amount
std::cin >> shipAmount; // store player entry to shipAmount
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
if (shipAmount < 1 || shipAmount > 5) // if the shipAmount isn't 1-5...
{
std::cout << "[SYSTEM] - Ship amount must be between 1-5." << std::endl; // tell player ship amount must be between 1-5
}
} while (shipAmount < 1 || shipAmount > 5); // loop if the shipAmount isn't between 1 and 5
player1 = new Player(shipAmount); // declares player1 as a new Player with the shipAmount
player2 = new Player(shipAmount); // declares player2 as a new Player with the shipAmount
std::cout << std::endl;
if(ai)
{
do
{
std::cout << "[SYSTEM] - Would you like to play\n1)Easy\n2)Medium\n3)Hard\nChoose(1-3): ";
std::cin >> ai_mode;
std::cout << std::endl;
while (std::cin.fail()) // while input failed
{
std::cin.clear(); // clear the cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
std::cout << "[SYSTEM] - Choose(1-3)\n1)EasyMode\n2)SmartMode\n3)Hacker Mode\nselection: "; // ask again for the mode
std::cin >> ai_mode; // store player entry to ai_mode
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
if (ai_mode < 1 || ai_mode > 3) // if the shipAmount isn't 1-3...
{
std::cout << "[SYSTEM] - Mode selection must be between 1-3." << std::endl; // tell player mode must be between 1-3
}
} while(ai_mode<1 || ai_mode>3);
}
obtainShips(); // calls the obtainShips function
playerGuess(); // calls the playerGuess function
}
void Game::obtainShips() // game class function 'obtainShips'
{
char tempxLetter; // declares char tempxLetter
int tempyNumber; // declares int tempyNumber
std::cout << "PLAYER 1 SHIP BOARD" << std::endl;
std::cout << "-------------------" << std::endl;
printBoard(); // prints the board
std::cout << std::endl; // creates a new line
/* PLAYER 1 STARTS HERE */
do // loop at least once
{
std::cout << "[SETUP] Player 1 - enter X coordinate for [1x1] ship (A-J): "; // ask player 1 for the x coordinate of their 1x1 ship
std::cin >> tempxLetter; // store player input to tempxLetter
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // if double input such as AA, will only take A as input
// std::cout << (char)tempxLetter << '\n';
if ((int)tempxLetter < 65 || (int)tempxLetter > 74) // if unicode for the player input isn't between 65 and 74 (A-J)
{
std::cout << "[SYSTEM] - Must enter letter between A and J." << std::endl; // inform player of the error
}
} while ((int)tempxLetter < 65 || (int)tempxLetter > 74); // then loop again
do // loop at least once
{
std::cout << "[SETUP] Player 1 - enter Y coordinate for [1x1] ship (1-10): "; // ask player for the y coordinate for their 1x1 ship
std::cin >> tempyNumber; // store player input
while (std::cin.fail()) // while the input is invalid...
{
std::cin.clear(); // clear cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
std::cout << "[SETUP] Player 1 - enter Y coordinate for 1x1 ship (1-10): "; // ask player again for the value
std::cin >> tempyNumber; // store player input
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
if (tempyNumber < 1 || tempyNumber > 10) // if the player input isn't between 1 and 10...
{
std::cout << "[SYSTEM] - Must enter number between 1 and 10." << std::endl; // inform player of error
}
} while (tempyNumber < 1 || tempyNumber > 10); // loop if input isn't between 1 and 10
player1->addShip(0, 0, tempxLetter, tempyNumber, 1, false); // temp line - Lee
player1->getShipInfo(0, 0); // temp line - Lee :: it seems like it is updating the information above function.
std::cout << std::endl;
std::cout << "PLAYER 1 SHIP BOARD" << std::endl;
std::cout << "-------------------" << std::endl;
player1->printShipBoard();
std::cout << std::endl;
/* Player 1 - Ship creation when shipAmount is more than 1 */
for (int i = 2; i <= shipAmount; i++) // for the second ship and each one after...
{
for (int j = 0; j < i; j++) // for each coordinate of the new ship...
{
int prevX, prevY; // declare ints prevX and prevY
/* Asking for X Coordinate */
bool isValidCoord = false; // declare bool isValidCoord initialized to false
/* Check if coordinates are valid */
do
{
do // run at least once
{
std::cout << "[SETUP] Player 1 - enter X coordinate " << (j + 1) << " for [1x" << i << "] ship (A-J): "; // ask player one for ships x coordinate
std::cin >> tempxLetter; // store player 1 x coord input
// Input Check (1) :: if input is not single char
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // if double input such as AA, will only take A as input
// Input Check (2) :: if input is not in range
if ((int)tempxLetter < 65 || (int)tempxLetter > 74) // if stored value isn't between A and J...
{
std::cout << "[SYSTEM] - Must enter letter between A and J." << std::endl; // inform player of the error
}
} while ((int)tempxLetter < 65 || (int)tempxLetter > 74); // loop while the error persists
/* Asking for Y Coordinate */
do
{
std::cout << "[SETUP] Player 1 - enter Y coordinate " << (j + 1) << " for [1x" << i << "] ship (1-10): "; // ask player one for ships y coordinate
std::cin >> tempyNumber; // store player 1 y coord input
while (std::cin.fail()) // if input is invalid
{
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore duplicate inputs
std::cout << "[SETUP] Player 1 - enter Y coordinate " << (j + 1) << " for [1x" << i << "] ship (1-10): ";
std::cin >> tempyNumber;
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // duplicate failsafe
if (tempyNumber < 1 || tempyNumber > 10) // if player y coord input isn't between 1 and 10...
{
std::cout << "[SYSTEM] - Must enter number between 1 and 10." << std::endl; // inform player of the error
}
} while (tempyNumber < 1 || tempyNumber > 10); // run while the error persists
// *need to add condition check if coord overlaps with prev ship coords*
if (j == 0) // when it is first time entering coordinate with multiple coord ship
{
if (isOverlapCoord(player1,tempxLetter, tempyNumber)) // if the input coordinates overlap with pre-exsisting ones
{
isValidCoord = false; // set coords to invalid
std::cout << "[SYSTEM] - Coordinate overlapes with existing coordinate!" << std::endl; // inform player what the error is
std::cout << "[SYSTEM] - Overlapping Coordinate : " << tempxLetter << ", " << tempyNumber << std::endl; // inform player of the incorrect coordinate
}
else // otherwise...
{
isValidCoord = true; // sets coords to valid
}
}
else // otherwise...
{
if ( // Input Check (4) :: Coord is next to previous coordinate (e.g. should not be A1, A3)
// if coordinates are not next to previous coordinates
((abs((int)tempxLetter - prevX > 1)) || (abs(tempyNumber - prevY) > 1))
// if coordinates overlap with previous coord
|| ((abs((int)tempxLetter - prevX) == 0) && (abs(tempyNumber - prevY) == 0))
)
{
std::cout << "[SYSTEM] - Must enter a new coordinate that is next to your previous coordinate." << std::endl; // inform player what the error is
std::cout << "[SYSTEM] - Previous Coordinate : " << (char)prevX << ", " << prevY << std::endl; // inform player of the pervious coordinate
}
else // otherwise...
{
if (!isOverlapCoord(player1,tempxLetter, tempyNumber)) // if coordinates are not overlapping...
{
isValidCoord = true; // sets coords to valid
}
else // otherwise...
{
std::cout << "[SYSTEM] - Coordinate overlapes with existing coordinate!" << std::endl; // inform player what the error is
std::cout << "[SYSTEM] - Overlapping Coordinate : " << tempxLetter << ", " << tempyNumber << std::endl; // inform player of the overlapping coordinates
}
}
}
} while (!isValidCoord); // loop if coordinates are invalid
prevX = (int)tempxLetter; // set prevX to the ASCII of tempxLetter
prevY = tempyNumber; // set prevY to tempynumber
player1->addShip(i - 1, j, tempxLetter, tempyNumber, i, false); // temp line - Lee
player1->getShipInfo(i - 1, j); // temp line - Lee :: it seems like it is updating the information above function.
std::cout << std::endl; // new line
std::cout << "PLAYER 1 SHIP BOARD" << std::endl; // tell player who's board it is
std::cout << "-------------------" << std::endl; // extra spacing
player1->printShipBoard(); // print player1s board
std::cout << std::endl; // new line
}
}
if(!ai)
{
std::cout << std::endl; // new ling
for (int i = 0; i < 100; i++) // loop 100 times
{
std::cout << std::endl; // create a new line (effectively clearing the command prompt)
}
/* PLAYER 2 STARTS HERE */
std::cout << "PLAYER 2 SHIP BOARD" << std::endl; // tell player who's board it is
std::cout << "-------------------" << std::endl; // extra spacing
printBoard(); // print the default board
do // loop at least once
{
std::cout << "[SETUP] Player 2 - enter X coordinate for 1x1 ship (A-J): "; // ask for x coordinate of 1x1 ship
std::cin >> tempxLetter; // store player input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // if double input such as AA, will only take A as input
if ((int)tempxLetter < 65 || (int)tempxLetter > 74) // if ASCII of player input isn't between 65 and 74 (A - J)...
{
std::cout << "[SYSTEM] - Must enter letter between A and J." << std::endl; // inform player of the error
}
} while ((int)tempxLetter < 65 || (int)tempxLetter > 74); // loop while input is incorrect
do // loop at least once
{
std::cout << "[SETUP] Player 2 - enter Y coordinate for [1x1] ship (1-10): "; // ask for y coordinate of 1x1 ship
std::cin >> tempyNumber; // store value
while (std::cin.fail()) // while input fails...
{
std::cin.clear(); // clear cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double inputs
std::cout << "[SETUP] Player 2 - enter Y coordinate for [1x1] ship (1-10): "; // ask again
std::cin >> tempyNumber; // store
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore double input values
if (tempyNumber < 1 || tempyNumber > 10) // if stored y value isn't between 1 and 10...
{
std::cout << "[SYSTEM] - Must enter number between 1 and 10." << std::endl; // inform player of the error
}
} while (tempyNumber < 1 || tempyNumber > 10); // loop while error persists
player2->addShip(0, 0, tempxLetter, tempyNumber, 1, false); // temp line - Lee
player2->getShipInfo(0, 0); // temp line - Lee :: it seems like it is updating the information above function.
std::cout << std::endl; // new line
std::cout << "PLAYER 2 SHIP BOARD" << std::endl; // print information for whos board
std::cout << "-------------------" << std::endl; // extra spacing
player2->printShipBoard(); // print player 2s board
std::cout << std::endl; // new line
/* PLAYER 2 When ship amount is more than one */
for (int i = 2; i <= shipAmount; i++) // for the second ship and each one after...
{
for (int j = 0; j < i; j++) // for the length of that ship...
{
int prevX, prevY; // declare ints 'prevX' and 'prevY'
/* Asking for X Coordinate */
bool isValidCoord = false; // declare bool 'isValidCoord' initialized to false
do // loop at least once
{
do // loop at least once
{
std::cout << "[SETUP] Player 2 - enter X coordinate " << (j + 1) << " [for 1x" << i << "] ship (A-J): "; // ask for the x coordinates
std::cin >> tempxLetter; // store player input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // if double input such as AA, will only take A as input
if ((int)tempxLetter < 65 || (int)tempxLetter > 74) // if input isn't between A and J...
{
std::cout << "[SYSTEM] - Must enter letter between A and J." << std::endl; // inform player of the error
}
} while ((int)tempxLetter < 65 || (int)tempxLetter > 74); // loop while error persists
/* Asking for Y Coordinate */
do // loop at least once
{
std::cout << "[SETUP] Player 2 - enter Y coordinate " << (j + 1) << " for [1x" << i << "] ship (1-10): "; // ask for y coordinate
std::cin >> tempyNumber; // store player input
while (std::cin.fail()) // if input fails
{
std::cin.clear(); // clear cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // remove duplicate inputs
std::cout << "[SETUP] Player 2 - enter Y coordinate " << (j + 1) << " for [1x" << i << "] ship (1-10): "; // ask for y coordinate again
std::cin >> tempyNumber; // store player input
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore duplicate inputs
if (tempyNumber < 1 || tempyNumber > 10) // if y coord isn't between 1 and 10...
{
std::cout << "[SYSTEM] - Must enter number between 1 and 10." << std::endl; // inform player of the error
}
} while (tempyNumber < 1 || tempyNumber > 10); // loop while error persists
// *need to add condition check if coord overlaps with prev ship coords*
if (j == 0) // when it is first time entering coordinate with multiple coord ship
{
if (isOverlapCoord(player2,tempxLetter, tempyNumber)) // if coordinates overlap...
{
isValidCoord = false; // set coordinates as invalid
std::cout << "[SYSTEM] - Coordinate overlapes with existing coordinate!" << std::endl; // inform player of overlapping coords
std::cout << "[SYSTEM] - Overlapping Coordinate : " << tempxLetter << ", " << tempyNumber << std::endl; // inform player of which coordinates
}
else // otherwise...
{
isValidCoord = true; // set coordinates as valid
}
}
else // otherwise...
{
if ( // Input Check (4) :: Coord is next to previous coordinate (e.g. should not be A1, A3)
// if coordinates are not next to previous coordinates
((abs((int)tempxLetter - prevX > 1)) || (abs(tempyNumber - prevY) > 1))
// if coordinates overlap with previous coord
|| ((abs((int)tempxLetter - prevX) == 0) && (abs(tempyNumber - prevY) == 0))
)
{
std::cout << "[SYSTEM] - Must enter a new coordinate that is next to your previous coordinate." << std::endl; // inform player of the error
std::cout << "[SYSTEM] - Previous Coordinate : " << (char)prevX << ", " << prevY << std::endl; // tells player the previous coordinates
}
else // otherwise...
{
if (!isOverlapCoord(player2,tempxLetter, tempyNumber)) // if coordinates are not overlapping
{
isValidCoord = true; // set coordinates as valid
}
else // otherwise...
{
std::cout << "[SYSTEM] - Coordinate overlapes with existing coordinate!" << std::endl; // inform player of the error
std::cout << "[SYSTEM] - Overlapping Coordinate : " << tempxLetter << ", " << tempyNumber << std::endl; // inform player which coordinates overlap
}
}
}
} while (!isValidCoord); // loop while coordinates are invalid
prevX = (int)tempxLetter; // sets 'prevX' as the ASCII of 'tempxLetter'
prevY = tempyNumber; // sets 'prevY' as 'tempyNumber'
player2->addShip(i - 1, j, tempxLetter, tempyNumber, i, false); // temp line - Lee
player2->getShipInfo(i - 1, j); // temp line - Lee :: it seems like it is updating the information above function.
std::cout << std::endl; // new line
std::cout << "PLAYER 2 SHIP BOARD" << std::endl; // informs player on whos board it is
std::cout << "-------------------" << std::endl; // extra spacing
player2->printShipBoard(); // print player 2s board
std::cout << std::endl; // new line
}
}
for (int i = 0; i < 100; i++) // loop 100 times
{
std::cout << std::endl; // new line (effectively clears command prompt)
}
}
else// our ai stuff
{
char convertLetter[] = {'A','B','C','D','E','F','G','H','I','J'}; //this array is called in order to convert a number into a char
char randX; //variable that will store the letter of the column where the ai is placing their ship
int randY = 0; //variable that will store the number of the row where the ai is placing their ship
int index; //variable used to convert a chart back into an int
bool isValidCoord = false; // bool to check if the index is a valid coordinate
bool vertical = false; //bool to check if the ai has begun placing their ships horizontal or vertical
char previousX; //stores the previous column letter of the index placed
int previousY = 0; //stores the previous row number of the index placed
char orientation; //states what direction the ai should place their ships (either horizontal vertical or either)
std::random_device rd; // obtain a random number from hardware
std::mt19937 gen(rd()); // seed the generator
std::uniform_int_distribution<> distr_x(0, 9); // define the range for the random numbers
std::uniform_int_distribution<> distr_y(1, 10); // define the range for the random numbers
randX = convertLetter[distr_x(gen)]; //Ai places ship 1 at any X coordinate (A-J)
randY = distr_y(gen); //Ai places ship 1 at any Y coordinate (1-10)
/* addShip funtion was modified to add a boolean to know it's a player or Ai's ship*/
player2->addShip(0, 0, randX, randY, 1, true); // adds ship to ai's board
player2->getShipInfo(0, 0); // outputs to user what index the ai placed their ship (will delete later)
std::cout << std::endl; // new line
std::cout << "AI SHIP BOARD" << std::endl;
std::cout << "-------------------" << std::endl;
player2->printShipBoard(); // print ais board (will delete later)
std::cout << std::endl; // new line
/* AI - Ship creation when shipAmount is more than 1 */
for (int i = 2; i <= shipAmount; i++) // for the second ship and each one after...
{
for (int j = 0; j < i; j++) // for each coordinate of the new ship...
{
if(j==0)//if a new ship is being placed
{
do
{
randX = convertLetter[distr_x(gen)]; //Ai generates random x coordinate (letter)
randY = distr_y(gen); //Ai generates random y coordinate (number)
orientation = EnoughSpace(randX, randY, i);
} while (isOverlapCoord(player2, randX, randY) || orientation=='O');//the ai will continue to generate coordinates until an empty and valid index is found
previousX = randX; //store where the x coordinate was placed
previousY = randY; //store where the y coordinate was placed
player2->addShip(i - 1, j, randX, randY, i, true); //add the coordinate to the ai's board
player2->getShipInfo(i - 1, j); // outputs to user what index the ai placed their ship (will delete later)
std::cout << std::endl; // new line
std::cout << "AI SHIP BOARD" << std::endl; // informs player on whos board it is
std::cout << "-------------------" << std::endl; // extra spacing
player2->printShipBoard(); // print ais board (will delete later)
std::cout << std::endl; // new line
}
else if(j==1)//if the second coordinate of a ship is being placed
{
do
{
randX = convertLetter[distr_x(gen)]; //Ai generates random x coordinate (letter)
randY = distr_y(gen); //Ai generates random y coordinate (number)
for(int k=0; k <= 9; k++) //loop to go through every possible letter
{
if(randX == convertLetter[k])//if the random letter generated is equal to one of the possible letters...
{
index = k;//store what position in the array that random letter is
}
}
if(orientation == 'B')
{
std::uniform_int_distribution<> v_or_h(0,1);
if(v_or_h(gen))
{
if( (abs(randY-previousY)==1) && (previousX == randX))//if the second value of the ship being placed is one row above or below the previous row and in the same column...
{
isValidCoord = true; //this is a valid coordinate
vertical = true; //record that the ai is starting to place their ships vertically
}
}
else
{
if( (abs(randY-previousY)==0) && ((previousX == convertLetter[index+1]) || (previousX == convertLetter[index-1])) && ((index-1)>=0) && ((index+1)<=9))//if the second value is in the same row as the original coordinate and one column to the left/right...
{
isValidCoord = true;//this is a valid coordinate
vertical = false;//record that the ai is starting to placed their ships horizontally
}
}
}
else if(orientation == 'V')
{
if( (abs(randY-previousY)==1) && (previousX == randX))//if the second value of the ship being placed is one row above or below the previous row and in the same column...
{
isValidCoord = true; //this is a valid coordinate
vertical = true; //record that the ai is starting to place their ships vertically
}
}
else if(orientation == 'H')
{
if( (abs(randY-previousY)==0) && ((previousX == convertLetter[index+1]) || (previousX == convertLetter[index-1])) && ((index-1)>=0) && ((index+1)<=9))//if the second value is in the same row as the original coordinate and one column to the left/right...
{
isValidCoord = true;//this is a valid coordinate
vertical = false;//record that the ai is starting to placed their ships horizontally
}
}
}while(!isValidCoord || isOverlapCoord(player2, randX, randY));//this function will continue to run if the ai did not generate a valid coordinate or if the coordinate generated overlaps with a previous coordinate
previousX = randX; //store where the x coordinate was placed
previousY = randY; //store where the y coordinate was placed
isValidCoord = false; //reset isValidCoord back to false
player2->addShip(i - 1, j, randX, randY, i, true); //add the coordinate to the ai's board
player2->getShipInfo(i - 1, j); // outputs to user what index the ai placed their ship (will delete later)
std::cout << std::endl; // new line
std::cout << "AI SHIP BOARD" << std::endl; // informs player on whos board it is
std::cout << "-------------------" << std::endl; // extra spacing
player2->printShipBoard(); // print ai's board (will delete later)
std::cout << std::endl; // new line
}
else//if the third, fourth, or fifth coordinate of a ship is being placed
{
do
{
randX = convertLetter[distr_x(gen)]; //Ai generates random x coordinate (letter)
randY = distr_y(gen); //Ai generates random y coordinate (letter)
for(int k=0; k <= 9; k++) //loop to go through every possible letter
{
if(randX == convertLetter[k])//if the random letter generated is equal to one of the possible letters...
{
index = k; //store what position in the array that random letter is
}
}
if(vertical)//if the second coordinate was placed vertically to the original
{
if( (abs(randY-previousY)==1) && (previousX == randX) ) //if the second value of the ship being placed is one row above or below the previous row and in the same column...
{
isValidCoord = true; //this is a valid coordinate
}
else if(isVerticalTo(player2, randX, randY, i))//if the the ship is being blocked by another ship or the border of the board, then try placing in the other direction to fit the rest of the ship
{
isValidCoord = true; //this is a valid coordinate
}
else
{
isValidCoord = false; //this is not a valid coordinate
}
}
else // if the second coordinate was placed horizontally to the original
{
if(abs(randY-previousY)==0)//if the second value is in the same row as the original coordinate
{
if( (previousX == convertLetter[index+1]) && ((index+1)<=9) )//if the coordiante is to the right of the original index
{
isValidCoord = true;//this is a valid coordinate
}
else if( (previousX == convertLetter[index-1]) && ((index-1)>=0) )//if the coordinate is to the left of the original index
{
isValidCoord = true;//this is a valid coordinate
}
else if(isHorizontalTo(player2, randX, randY, i))//if the coordinate is either directly to the left or right of the previous ship coordinates
{
isValidCoord = true;//this is a valid coordinate
}
else
{
isValidCoord = false;//this is not a valid coordinate
}
}
else
{
isValidCoord = false;//this is not a valid coordinate
}
}
}while(!isValidCoord || isOverlapCoord(player2, randX, randY));//if the generated coordinate is not valid or overlaps with another ship then generate two coordinates again
previousX = randX; //store where the x coordinate was placed
previousY = randY; //store where the y coordinate was placed
isValidCoord = false; //reset isValidCoord back to false
player2->addShip(i - 1, j, randX, randY, i, true); //add the coordinate to the ai's board
player2->getShipInfo(i - 1, j); // outputs to user what index the ai placed their ship (will delete later)
std::cout << std::endl; // new line
std::cout << "AI SHIP BOARD" << std::endl; // informs player on whos board it is
std::cout << "-------------------" << std::endl; // extra spacing
player2->printShipBoard(); // print ai's board (will delete later)
std::cout << std::endl; // new line
}
}
}
}
}
char Game::EnoughSpace(char xLetter, int yNumber, int shipNumber)//check if there is enough space to fit the entire ship before starting to place it
{
bool cont = true;//for loop will continue until 'cont' is set to false
int aboves = 0;//counts how many valid spaces are above the index
int belows = 0;//counts how many valid spaces are below the index
int lefts = 0;//counts how many valid spaces are to the left of the index
int rights = 0;//counts how many valid spaces are to the right of the index
char convertLetter[] = {'A','B','C','D','E','F','G','H','I','J'};
int test = 0;
//above counter
for(int i=1; cont; i++)
{
if( ((yNumber-i)<1) || (player2->ExistingShip(xLetter, yNumber-i)) )//if a ship exists above or the game board ends
{
cont = false;
}
else
{
aboves++;
}
}
cont = true;//reset cont for next counter
//below counter
for(int i=1; cont; i++)
{
if( ((yNumber+i)>10) || (player2->ExistingShip(xLetter, yNumber+i)) )//if a ship exists below or the game board ends
{
cont = false;
}
else
{
belows++;
}
}
cont = true;//reset cont for next counter
//right counter
for(int i=1; cont; i++)
{
test = ConvertToNumber(xLetter)+i;
if( ( (ConvertToNumber(xLetter)+1+i) >10 ) || (player2->ExistingShip(convertLetter[test], yNumber)) )//if a ship exists to the right or the game board ends
{
cont = false;
}
else
{
rights++;
}
}
cont = true;//reset cont for next counter
//left counter
for(int i=1; cont; i++)
{
test = ConvertToNumber(xLetter)-i;
if( ( (ConvertToNumber(xLetter)+1-i) <1 ) || (player2->ExistingShip(convertLetter[test], yNumber)) )//if a ship exists to the left or the game board ends
{
cont = false;
}
else
{
lefts++;
}
}
cont = true;//reset cont for next counter
//both counter
if( ((aboves+belows+1) >= shipNumber) && ((lefts+rights+1) >= shipNumber))//if there is enough valid spaces to place the ship horizontal or vertical than return 'B' for both
{
return('B');
}
else if( (lefts+rights+1) >= shipNumber )//if there is only enough valid spaces to place the ship horizontally than return 'H' for horizontal
{
return('H');
}
else if( ((aboves+belows+1) >= shipNumber) )//if there is only enough valid spaces to place the ship vertically than return 'V' for vertical
{
return('V');
}
return('O');//there is no orientation that will allow the ai to finish placing this ship so new coordinates should be generated
}
bool Game::isHorizontalTo(Player* thisPlayer, char xLetter, int yNumber, int shipNumber) //checks if the ai has started to place their ships horizontally
{
char convertLetter[] = {'A','B','C','D','E','F','G','H','I','J'};//used to convert char to int
int index = 0;//used to convert random generated column letter to an int
int index_2 = 0;//used to convert previously placed ship column letters to an int
for(int k=0; k <= 9; k++)//loop to go through every possible letter
{
if(xLetter == convertLetter[k]) //if the random letter generated is equal to one of the possible letters...
{
index = k;//store what position in the array that random letter is
}
}
for (int i = 0; i < shipAmount; i++) // for each ship
{
for (int j = 0; j < (i + 1); j++) // for each position of coordinates
{
if(i==shipNumber-1)// if trying to place a certain ship, we only care about where the previous coordinates of that ship are
{
for(int k=0; k<=9; k++)//loop to go through every possible letter
{
if(thisPlayer->getShip(i)->getXCoord(j) == convertLetter[k])//if the previously placed coordinates of a ship is equal to one of the possible letters...
{
index_2 = k;//store what position in the array that coordinate is
}
}
if (thisPlayer->getShip(i)->getYCoord(j) == yNumber && index_2 == index-1) //if the random generated y coordinate is in the same row as the previous coordinates and the random generated x coordinate is 1 column to the left of the previously placed ships...
{
return true; //this is a valid coordinate to place the next ship coordinate
}
else if(thisPlayer->getShip(i)->getYCoord(j) == yNumber && index_2 == index+1) //if the random generated y coordinate is in the same row as the previous coordinates and the random generated x coordinate is 1 column to the right of the previously placed ships...
{
return true;//this is a valid coordinate to place the next ship coordinate
}
}
}
}
return false; // this is not a valid coordinate to place the next ship coordinate and thus another two values should be generated
}
int Game::ConvertToNumber(char letter)
{
char convertLetter[] = {'A','B','C','D','E','F','G','H','I','J'};//used to convert char to int
for(int k=0; k <= 9; k++)//loop to go through every possible letter
{
if(letter == convertLetter[k]) //if the random letter generated is equal to one of the possible letters...
{
return k;
}
}
}
bool Game::isVerticalTo(Player* thisPlayer, char xLetter, int yNumber, int shipNumber)
{
for (int i = 0; i < shipAmount; i++) // for each ship
{
for (int j = 0; j < (i + 1); j++) // for each position of coordinates
{
if(i==shipNumber-1)// if trying to place a certain ship, we only care about where the previous coordinates of that ship are
{
if (thisPlayer->getShip(i)->getXCoord(j) == xLetter && thisPlayer->getShip(i)->getYCoord(j) == yNumber-1) //if the x coordinate being placed is in the same column as the other previously placed coordinates and one row above the previously placed coordinate
{
return true; //this is a valid coordinate to place the next ship coordinate
}
else if(thisPlayer->getShip(i)->getXCoord(j) == xLetter && thisPlayer->getShip(i)->getYCoord(j) == yNumber+1) //if the x coordinate being placed is in the same column as the other previously placed coordinates and one row below the previously placed coordinate
{
return true; //this is a valid coordinate to place the next ship coordinate
}
}
}
}
return false; // this is not a valid coordinate to place the next ship coordinate and thus another two values should be generated
}
bool Game::isOverlapCoord(Player* thisPlayer, char xLetter, int yNumber) // game class 'isOverlapCoord' function inputs the player class and coordinates and returns bool if they overlap
{
// Input Check (3) :: if input overlaps with coordinates of existing ship
for (int i = 0; i < shipAmount; i++) // for each ship
{
for (int j = 0; j < (i + 1); j++) // for each position of coordinates
if (thisPlayer->getShip(i)->getXCoord(j) == xLetter && thisPlayer->getShip(i)->getYCoord(j) == yNumber) // if the coordinates overlap
{
return true; // returns true
}
}
return false; // returns false
}
void Game::playerGuess() // game class 'playerGuess' function that asks for player coordinate input and then applies it to the relevant game board
{
char xGuess; // declares char 'xGuess'
int yGuess; // declares int 'yGuess'
char orig_x;
int orig_y;
bool guess_advance = false;
bool advance_right = false;
bool advance_left = false;
bool advance_up = false;
bool advance_down = false;
int x_index;
int y_index;
char convertLetter[] = {'A','B','C','D','E','F','G','H','I','J'};//used to convert char to int
/* PLAYER 1 : Guessing Coordinates */
while (!gameFinished) // while the game is still going...
{
for (int i = 0; i < 100; i++) // loop 100 times
{
std::cout << std::endl; // new line (effectively clears the command prompt)
}
std::cout << "PLAYER 1 ATTACK BOARD (R=hit W=miss)" << std::endl; // inform which board and a basic key
std::cout << "------------------------------------" << std::endl; // extra spacing
player1->printAttackBoard(); // print player 1s board
printScore(player1, player2);
std::cout << std::endl; // new line
do // loop at least once
{
std::cout << "[ATTACK] Player 1 - enter X coordinate (A-J): "; // ask for x coordinate
std::cin >> xGuess; // store player input
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // if double input such as AA, will only take A as input
if ((int)xGuess < 65 || (int)xGuess > 74) // if ASCII of player input isn't between 65 and 74 (A - J)...
{
std::cout << "Must enter letter between A and J\n"; // inform player of the error
}
} while ((int)xGuess < 65 || (int)xGuess > 74); // loop while error persists
// std::cout << xGuess << '\n';
do // loop at least once
{
std::cout << "[ATTACK] Player 1 - enter Y coordinate (1-10): "; // ask player for y coordinate
std::cin >> yGuess; // store player input
while (std::cin.fail()) // loop while an error occurs
{
std::cin.clear(); // clear cache
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // test for duplicate inputs
std::cout << "[ATTACK] Player 1 - enter Y coordinate (1-10): "; // ask to re-enter y coordinate
std::cin >> yGuess; // store player input
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // ignore duplicates
if (yGuess < 1 || yGuess > 10) // if player input isn't between 1 and 10...
{
std::cout << "Must enter number between 1 and 10\n"; // inform player of the error
}