-
Notifications
You must be signed in to change notification settings - Fork 0
/
module2Main.cpp
executable file
·741 lines (673 loc) · 21.4 KB
/
module2Main.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
/*
Triad Development Group
Battleship console game
Module 2 - Gameplay Module
*/
/*
PrintHighScores functionality:
Throughout the game, we keep a running total of the shots taken by each player and the number of hits for each player.
In this way, we can calculate the hit percentage at the end of the game.
For the purposes of calculating the player's score, we will use the following procedure:
-each time the player gets a hit, they get 10 points
-each time the player sinks a ship, they get points equal to the ship bonus (10x the length of the ship)
-at the end of the game, the player's score is multiplied by 100% plus their hit percentage (eg. *175% for a hit percentage of 75%)
*/
//Included Libraries
#include<iostream>
#include<ctime>
#include<climits>
#include<string>
#include<stdlib.h>
#include<stdio.h>
#include <sstream>
#include <cctype>
#include <iomanip>
#include <fstream>
#include <cstdlib>
#include <cstring>
typedef char grid[MAX_ROW_LABEL][MAX_ROW_LABEL];
using namespace std;
//Declarations of non-class functions native to THIS MODULE ONLY
/*saveGame
Description: saveGame takes a snapshot of the current gamestate and writes it to a file that can be
read and interpreted at a later time if loadGame is called
Pre-: expects to be given a string containing the player's name, as well as the usergrid and cpugrid
which probably contain letters for ships, hits, and misses
Post-: the file is written in the game directory and contains all of the information the function
was passed. After the function is called, the program terminates.
*/
void saveGame(string, grid, grid);
//Class Declarations
/*class ship
Description: The ship class is the class that defines all of the ship objects.
It contains their member variables, which describe properties the ships should have.
It also defines the member functions to access these properties and manage basic operation
of the ship objects.
*/
class ship {
private:
int health; //number of units not hit yet
int length;
bool isSunk; //whether or not all units have been hit
public:
void setHealth(int);
int checkHealth();
void setSunk(bool);
bool checkSunk();
void hit();
};
/*class player
Description: The player class is the class that defines both player objects.
It contains the member variables that describe properties of the player, which include
their stats such as score and hit percentage.
It also defines the member functions to access these properties and manage basic operation
of the player objects.
*/
class player {
private:
int type; //what kind of user? 0 for cpu. 1 for human.
int health; //total of all ship health
int score; //score according to procedure listed at TOP
int shots; //number of total shots player has taken
int hits; //number of hits the player has achieved
double hitPercent; //hits/shots
string name; //name of player (only used for human)
public:
void setType(int);
int checkType();
void setHealth(int);
int checkHealth();
void decrementHealth();
char takeTurn(string, grid, grid, grid, grid);
bool verifyShot(string, grid, grid, grid, char, int);
void addScore(int);
int checkScore();
void setScore(int);
void incrementShots();
void setShots(int);
int checkShots();
void incrementHits();
void setHits(int);
int checkHits();
void calcPercent();
double checkPercent();
void genScore();
void setName(string);
};
//BEGIN LOAD-GAME FUNCTIONALITY
int playGame(string name, grid usergrid, grid cpugrid) {
//declare base objects
player user, cpu;
user.setScore(0);
cpu.setScore(0);
user.setType(1);
cpu.setType(0);
user.setName(name);
ship userCar, cpuCar, userBat, cpuBat, userDes, cpuDes, userSub, cpuSub,
userPat, cpuPat;
char selected;
char selected2;
string discardstr;
grid userview;
createGrid(userview);
//declare and initialize counters for loading ships, hits, and misses
int unumC = 0, unumB = 0, unumD = 0, unumS = 0, unumP = 0; //counters for numbers of each letter on grid (i.e. health of corresponding ship)
int cnumC = 0, cnumB = 0, cnumD = 0, cnumS = 0, cnumP = 0;
int unumX = 0, unumM = 0, cnumX = 0, cnumM = 0; //counters for numbers of hits and misses for each player
//loop to count instances in both grids
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
if (cpugrid[i][j] == 'M') { //if the player missed at this spot
userview[i][j] = 'M';
unumM++;
} else if (cpugrid[i][j] == 'X') { //if the player hit at this spot
userview[i][j] = 'X';
unumX++;
} else if (cpugrid[i][j] == 'C') { //if the cpu has an aircraft carrier here
cnumC++;
} else if (cpugrid[i][j] == 'B') { //" battleship
cnumB++;
} else if (cpugrid[i][j] == 'D') { //" destroyer
cnumD++;
} else if (cpugrid[i][j] == 'S') { //" submarine
cnumS++;
} else if (cpugrid[i][j] == 'P') { //" patrol boat
cnumP++;
}
if (usergrid[i][j] == 'M') { //if the computer missed at this spot
cnumM++;
} else if (usergrid[i][j] == 'X') { //if the computer hit at this spot
cnumX++;
} else if (usergrid[i][j] == 'C') { //if the user has an aircraft carrier here
unumC++;
} else if (usergrid[i][j] == 'B') { //" battleship
unumB++;
} else if (usergrid[i][j] == 'D') { //" destroyer
unumD++;
} else if (usergrid[i][j] == 'S') { //" submarine
unumS++;
} else if (usergrid[i][j] == 'P') { //" patrol boat
unumP++;
}
}
}
//set ships health
cpuCar.setHealth(cnumC);
cpuBat.setHealth(cnumB);
cpuDes.setHealth(cnumD);
cpuSub.setHealth(cnumS);
cpuPat.setHealth(cnumP);
userCar.setHealth(cnumC);
userBat.setHealth(cnumB);
userDes.setHealth(cnumD);
userSub.setHealth(cnumS);
userPat.setHealth(cnumP);
//draw grids on screen first time
//system("clear");
cout
<< "*******************************************************************"
<< endl;
cout
<< " BATTLESHIP "
<< endl;
cout
<< "*******************************************************************"
<< endl;
cout << endl << endl;
printGrid(userview);
cout
<< "-------------------------------------------------------------------"
<< endl;
printGrid(usergrid);
//if no hits or misses, make sure ships health is correct
if (unumM == 0 && unumX == 0) {
userCar.setHealth(5);
userBat.setHealth(4);
userDes.setHealth(3);
userSub.setHealth(3);
userPat.setHealth(2);
}
if (cnumM == 0 && cnumX == 0) {
cpuCar.setHealth(5);
cpuBat.setHealth(4);
cpuDes.setHealth(3);
cpuSub.setHealth(3);
cpuPat.setHealth(2);
}
//set initial number of shots and hits for each player
user.setHits(unumX);
user.setShots(unumX + unumM);
cpu.setHits(cnumX);
cpu.setShots(cnumX + cnumM);
//set initial player health
user.setHealth(
userCar.checkHealth() + userBat.checkHealth()
+ userDes.checkHealth() + userSub.checkHealth()
+ userPat.checkHealth());
cpu.setHealth(
cpuCar.checkHealth() + cpuBat.checkHealth() + cpuDes.checkHealth()
+ cpuSub.checkHealth() + cpuPat.checkHealth());
//set initial player scores
user.addScore(
10
* ((5 - cnumC) + (4 - cnumB) + (3 - cnumD) + (3 - cnumS)
+ (2 - cnumP)));
cnumC == 0 ? user.addScore(50) : user.addScore(0);
cnumB == 0 ? user.addScore(40) : user.addScore(0);
cnumD == 0 ? user.addScore(30) : user.addScore(0);
cnumS == 0 ? user.addScore(30) : user.addScore(0);
cnumP == 0 ? user.addScore(20) : user.addScore(0);
cpu.addScore(
10
* ((5 - unumC) + (4 - unumB) + (3 - unumD) + (3 - unumS)
+ (2 - unumP)));
unumC == 0 ? cpu.addScore(50) : cpu.addScore(0);
unumB == 0 ? cpu.addScore(40) : cpu.addScore(0);
unumD == 0 ? cpu.addScore(30) : cpu.addScore(0);
unumS == 0 ? cpu.addScore(30) : cpu.addScore(0);
unumP == 0 ? cpu.addScore(20) : cpu.addScore(0);
//END LOAD-GAME FUNCTIONALITY
while (user.checkHealth() > 0 && cpu.checkHealth() > 0) { //while both players are still alive
selected = user.takeTurn(name, usergrid, cpugrid, cpugrid, userview); //take the human player's turn and save the letter at the position they select
//decide if the selection is a hit or a miss, and update the appropriate ship and player health and score
switch (selected) {
case '.':
cout << "MISS!" << endl;
break;
case 'C':
cout << "HIT!" << endl;
user.incrementHits();
cpuCar.hit();
cpu.decrementHealth();
user.addScore(10);
if (cpuCar.checkHealth() == 0) {
cout << "You have sunk the opponent's Aircraft Carrier!"
<< endl;
cpuCar.setSunk(true);
user.addScore(50);
}
break;
case 'B':
cout << "HIT!" << endl;
user.incrementHits();
cpuBat.hit();
cpu.decrementHealth();
user.addScore(10);
if (cpuBat.checkHealth() == 0) {
cout << "You have sunk the opponent's Battleship!" << endl;
cpuBat.setSunk(true);
user.addScore(40);
}
break;
case 'D':
cout << "HIT!" << endl;
user.incrementHits();
cpuDes.hit();
cpu.decrementHealth();
user.addScore(10);
if (cpuDes.checkHealth() == 0) {
cout << "You have sunk the opponent's Destroyer!" << endl;
cpuDes.setSunk(true);
user.addScore(30);
}
break;
case 'S':
cout << "HIT!" << endl;
user.incrementHits();
cpuSub.hit();
cpu.decrementHealth();
user.addScore(10);
if (cpuSub.checkHealth() == 0) {
cout << "You have sunk the opponent's Submarine!" << endl;
cpuSub.setSunk(true);
user.addScore(30);
}
break;
case 'P':
cout << "HIT!" << endl;
user.incrementHits();
cpuPat.hit();
cpu.decrementHealth();
user.addScore(10);
if (cpuPat.checkHealth() == 0) {
cout << "You have sunk the opponent's Patrol Boat!" << endl;
cpuPat.setSunk(true);
user.addScore(20);
}
break;
default:
cout << "OH NO SOMETHING WENT TERRIBLY WRONG!" << endl; //<----- This actually helped us locate several errors :)
break;
}
printGrid(userview); //print to the screen the human player's view at the end of their turn
//check if the player won on their last turn
if (cpu.checkHealth() == 0) {
cout << "Congratulations! You have won the game!" << endl;
cout << "\tYour stats were: " << endl;
cout << "\t\t" << user.checkShots() << " total shots" << endl;
cout << "\t\t" << user.checkHits() << " hits" << endl;
cout << "\t\t" << user.checkShots() - user.checkHits() << " misses"
<< endl;
user.genScore();
cout << "\tResulting in a hit percentage of " << setprecision(2)
<< user.checkPercent() << "%" << endl;
cout << "\tYou achieved a score of " << user.checkScore() << "!"
<< endl;
//if the player won on their last turn, initiate end-game procedure, including generating their final score
ofstream highscores;
highscores.open("highscores.txt", ios::app);
highscores << user.checkScore() << " " << name;
highscores.close();
cout << "Press any key to continue!" << endl;
cin >> discardstr;
return 1;
}
selected2 = cpu.takeTurn(name, usergrid, cpugrid, usergrid, userview); //take the computer player's turn and save the randomly selection position
//decide if the selection is a hit or a miss, and update the appropriate ship and player health and score
switch (selected2) {
case '.':
cout << "MISS!" << endl;
break;
case 'C':
cout << "HIT!" << endl;
cpu.incrementHits();
userCar.hit();
user.decrementHealth();
cpu.addScore(10);
if (userCar.checkHealth() == 0) {
cout << "Your opponent has sunk your Aircraft Carrier!" << endl;
userCar.setSunk(true);
cpu.addScore(50);
}
break;
case 'B':
cout << "HIT!" << endl;
cpu.incrementHits();
userBat.hit();
user.decrementHealth();
cpu.addScore(10);
if (userBat.checkHealth() == 0) {
cout << "Your opponent has sunk your Battleship!" << endl;
userBat.setSunk(true);
cpu.addScore(40);
}
break;
case 'D':
cout << "HIT!" << endl;
cpu.incrementHits();
userDes.hit();
user.decrementHealth();
cpu.addScore(10);
if (userDes.checkHealth() == 0) {
cout << "Your opponent has sunk your Destroyer!" << endl;
userDes.setSunk(true);
cpu.addScore(30);
}
break;
case 'S':
cout << "HIT!" << endl;
cpu.incrementHits();
userSub.hit();
user.decrementHealth();
cpu.addScore(10);
if (userSub.checkHealth() == 0) {
cout << "Your opponent has sunk your Submarine!" << endl;
userSub.setSunk(true);
cpu.addScore(30);
}
break;
case 'P':
cout << "HIT!" << endl;
cpu.incrementHits();
userPat.hit();
user.decrementHealth();
cpu.addScore(10);
if (userPat.checkHealth() == 0) {
cout << "Your opponent has sunk your Patrol Boat!" << endl;
userPat.setSunk(true);
cpu.addScore(20);
}
break;
default:
cout << "OH NO SOMETHING WENT TERRIBLY WRONG!" << endl; //<----- This actually helped us locate several errors :)
break;
}
printGrid(usergrid); //print to the screen the human player's view of the computer opponent's grid (i.e. only their own hits/misses)
//check if the human player lost on the computer's last turn, and if so, initiate end-game procedure
if (user.checkHealth() == 0) {
cout << "Oh no! You have lost the game!" << endl;
cout << "Press any key to continue!" << endl;
cin >> discardstr;
return 0;
}
}
return 0;
}
//sets the ship object's health
void ship::setHealth(int input) {
health = input;
}
//returns the ship object's health
int ship::checkHealth() {
return health;
}
//sets the ship object's sunk status
void ship::setSunk(bool input) {
isSunk = input;
}
//returns the ship object's sunk status
bool ship::checkSunk() {
return isSunk;
}
//decrements the ship object's health as a result of being hit
void ship::hit() {
health--;
}
//sets the player object's name
void player::setName(string input) {
name = input;
}
//sets the player object's type (0 for CPU, 1 for human)
void player::setType(int input) {
type = input;
}
//returns the player object's type
int player::checkType() {
return type;
}
//sets the player object's health (combined total of their ships' health)
void player::setHealth(int input) {
health = input;
}
//returns the player object's health
int player::checkHealth() {
return health;
}
//decrements the player object's health as a result of one of their ships being hit
void player::decrementHealth() {
health--;
}
//adds desired amount to the player object's score
void player::addScore(int input) {
score += input;
}
//returns the player object's current score
int player::checkScore() {
return score;
}
//sets the player object's score to the desired amount
void player::setScore(int input) {
score = input;
}
//increments the number of shots the player object has taken as when they take 1 turn
void player::incrementShots() {
shots++;
}
//sets the number of shots the player object has taken (used when loading an existing game)
void player::setShots(int input) {
shots = input;
}
//returns the player object's number of shots
int player::checkShots() {
return shots;
}
//increments the number of hits the player object has achieved
void player::incrementHits() {
hits++;
calcPercent();
}
//sets the number of shots the player object has taken (used when loading an existing game)
void player::setHits(int input) {
hits = input;
}
//returns the player object's number of hits
int player::checkHits() {
return hits;
}
//calculates the player object's hit percentage as a function of hits divided by total shots
void player::calcPercent() {
hitPercent = (double) hits / shots;
}
//returns the player object's hit percentage
double player::checkPercent() {
return hitPercent;
}
//generates the player object's final game score, taking into account the hit percentage
void player::genScore() {
score = (double) score * (1 + hitPercent);
}
/*player::takeTurn
Description: takeTurn is responsible for prompting the user for a position to fire at (or randomly
generating it in the case of the computer) and verifying that position as valid. It also places
a HIT (X) or MISS (M) on the appropriate grid.
Pre-: It expects the player object's name on which it is being called (because saveGame is called inside of here),
as well as 4 grids; the usergrid, cpugrid, and grid corresponding to which object called the function (one of
which is redundant), and the grid representing the user's view.
Post-: The player object's turn will have effectively been taken, they will have been prompted for a position to fire at
(or one will have been randomly generated in the case of the computer), and a HIT (X) or MISS (X) will be placed on the
appropriate grid.
*/
char player::takeTurn(string name, grid usergrid, grid cpugrid, grid Grid, grid view) {
char x;
int y=-1;
char selected;
if (type == 1) {
cout << "Please choose a position to fire at (in the form A1): ";
//cin >> x >> y;
cin >> x;
while (x == '?' || x == 'S' || x == 'Q') {
//if statement here for help guide
if (x == '?') {
PrintHelpGuide();
//system("clear");
cout
<< "*******************************************************************"
<< endl;
cout
<< " BATTLESHIP "
<< endl;
cout
<< "*******************************************************************"
<< endl;
cout << endl << endl;
printGrid(view);
cout
<< "-------------------------------------------------------------------"
<< endl;
printGrid(usergrid);
cout
<< "Please choose a position to fire at (in the form A1): ";
//cin >> x >> y;
} else if (toupper(x) == 'S') {
saveGame(name, usergrid, cpugrid);
} else if (toupper(x) == 'Q') {
char response;
cout << "Would you like to save the game first (Y/N)? ";
cin >> response;
while (toupper(response) != 'Y' && toupper(response) != 'N') {
cout << "Please enter Y or N!" << endl;
cout << "Would you like to save the game first (Y/N)? ";
cin >> response;
}
if (toupper(response) == 'Y') {
saveGame(name, usergrid, cpugrid);
} else {
quitGame();
}
}
cin >> x;
}
while(y == -1){
try{
cin >> y;
if(!cin)
throw 1;
}
catch (int a){
y = -1;
cin.clear();
string junk;
getline(cin,junk);
}
}
x=toupper(x);
while(verifyShot(name, usergrid, cpugrid, view, x, y)==false){
cout << "INVALID POSITION!" << endl << "Please choose a position to fire at (in the form A1): ";
cin >> x;
x=toupper(x);
try{
cin>> y;
if(!cin)
throw 2;
}
catch (int a){
y = -1;
cin.clear();
string junk;
getline(cin,junk);
}
}
//system("clear");
cout
<< "*******************************************************************"
<< endl;
cout
<< " BATTLESHIP "
<< endl;
cout
<< "*******************************************************************"
<< endl;
cout << endl << endl;
cout << "You fired at " << x << y << "... ";
incrementShots();
} else {
do {
x = rand() % 10 + 65;
y = rand() % 10;
} while (verifyShot(name, usergrid, cpugrid, Grid, x, y) == false);
cout
<< "-------------------------------------------------------------------"
<< endl;
cout << "The computer fired at " << x << y << "... ";
incrementShots();
}
x = toupper(x);
selected = Grid[y - 1][x - 65]; //As opposed to above commented out logic, because now takeTurn is passed the corresponding grid
if (type == 0) {
selected == '.' ? Grid[y - 1][x - 65] = 77 : Grid[y - 1][x - 65] = 88;
} else {
selected == '.' ? view[y - 1][x - 65] = 77 : view[y - 1][x - 65] = 88;
selected == '.' ? cpugrid[y - 1][x - 65] = 77 : cpugrid[y - 1][x - 65] =
88;
}
return selected;
}
/*player::verifyShot
Description: verifyShot is responsible for determining if a position selected to fire at is valid (i.e. it is on
the grid, and has not been fired at before.
Pre-: It expects the player object's name (since saveGame is called from here), as well as 3 grids; the usergrid,
cpugrid, and grid corresponding to which object called it (one of which is redundant), and finally the coordinates
of the position that has been selected to fire at.
Post-: It returns true if the position is valid to fire at (i.e. is on the grid and has not been fired at before).
It returns false if the position is not valid to fire at (i.e. is off the grid or has been fired at before).
*/
bool player::verifyShot(string name, grid usergrid,
grid cpugrid, grid grid, char x, int y) {
if (!(x >= 65 && x <= 74)) {
return false;
} else if (!(y >= 1 && y <= 10)) {
return false;
} else if (grid[y - 1][x - 65] == 'X') {
return false;
} else if (grid[y - 1][x - 65] == 'M') {
return false;
} else
return true;
}
/*saveGame
Description: saveGame is responsible for writing the current game-state information to a file in the game directory.
Pre-: It expects the player object's name and both the usergrid and cpugrid, all of which are written to a file in
the game directory called "saved.txt". The format is name/usergrid/cpugrid where / is a line break.
Post-: It returns nothing, but the file will be written in the game directory, ready to be loaded at a later date if
the user so chooses. When this function is called, and after it has saved the game, the program terminates.
*/
void saveGame(string name, grid usergrid, grid cpugrid) {
ofstream saved;
saved.open("saved.txt", ios::trunc);
saved << name << endl;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
saved << usergrid[i][j] << " ";
}
saved << endl;
}
cout << endl;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
saved << cpugrid[i][j] << " ";
}
saved << endl;
}
saved.close();
quitGame();
}