Advertisement
Guest User

Battleship

a guest
Nov 25th, 2019
210
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 36.43 KB | None | 0 0
  1. #include <iostream>
  2. #include <string> //string + string operations
  3. #include <cstdlib> //srand() and system("clear")
  4. #include <ctime> //time(0)
  5. #include <memory>
  6.  
  7. //Return a random number between min and max
  8. int getRandom(int min, int max)
  9. {
  10. //Set initial state of seeded for rand()
  11. static bool seeded = false;
  12. if(!seeded)
  13. {
  14. srand(time(0));
  15. seeded = true;
  16. }
  17.  
  18. return (rand()%(max-min+1)) + min;
  19. }
  20.  
  21. //Check to see if a string could be an integer
  22. bool isInt(std::string number, bool requireUnsigned=false)
  23. {
  24. bool result = false;
  25.  
  26. if(number.size() > 0)
  27. {
  28. result = true;
  29. unsigned startPos = 0;
  30.  
  31. //If the number starts with a hyphen, start the loop counter @[1]
  32. if(number[0] == '-')
  33. startPos = 1;
  34.  
  35. //Iterate through the string and set to false if input isn't an integer
  36. for(unsigned i = startPos; i<number.size(); i++)
  37. if(!isdigit(number[i]))
  38. result = false;
  39.  
  40. //Check for above zero when required
  41. if(requireUnsigned && number[0] == '-')
  42. result = false;
  43. }
  44.  
  45. return result;
  46. }
  47.  
  48. //Dynamically resizing arrays that accept any data type
  49. //Works with pointers, but it's the programmer's responsibility to delete memory
  50. template <class T>
  51. class dyn_array
  52. {
  53. private:
  54. unsigned size;
  55. T* array1;
  56. T* array2;
  57. public:
  58. unsigned getSize();
  59. dyn_array();
  60. dyn_array(const dyn_array&);
  61. dyn_array& operator=(const dyn_array&);
  62. void push(T);
  63. void deleteItem(int);
  64. void clear();
  65. T& operator[](int);
  66. ~dyn_array();
  67. };
  68. //Return the current size of the array
  69. template <class T>
  70. unsigned dyn_array<T>::getSize()
  71. {
  72. return this->size;
  73. }
  74. //Constructor that sets the initial array size and dynamically allocates an internal array
  75. template <class T>
  76. dyn_array<T>::dyn_array()
  77. {
  78. this->size = 0;
  79. this->array1 = new T[this->size+1];
  80. this->array2 = nullptr;
  81. }
  82. //Copy constructor to copy size, create a new array and copy its contents
  83. template <class T>
  84. dyn_array<T>::dyn_array(const dyn_array& obj)
  85. {
  86. this->size = obj.size;
  87. this->array1 = new T[this->size+1];
  88. for(unsigned i=0; i<this->size; i++)
  89. this->array1[i] = obj.array1[i];
  90.  
  91. this->array2 = nullptr;
  92. }
  93. //Overloaded = operator for copying
  94. template <class T>
  95. dyn_array<T>& dyn_array<T>::operator=(const dyn_array& obj)
  96. {
  97. //Check if it tries to copy itself
  98. if(this != &obj)
  99. {
  100. //Delete old array
  101. delete [] this->array1;
  102.  
  103. this->size = obj.size;
  104. this->array1 = new T[this->size+1];
  105. for(unsigned i=0; i<this->size; i++)
  106. this->array1[i] = obj.array1[i];
  107.  
  108. this->array2 = nullptr;
  109. }
  110.  
  111. return *this;
  112. }
  113. //Allow pushing new elements to the array
  114. template <class T>
  115. void dyn_array<T>::push(T data)
  116. {
  117. //Insert new element into the empty spot
  118. this->array1[this->size] = data;
  119. this->size++;
  120. this->array2 = new T[this->size+1];
  121.  
  122. //copy to array2 + 1
  123. for(unsigned i=0; i<this->size; i++)
  124. this->array2[i] = this->array1[i];
  125.  
  126. //Delete old array and switch
  127. delete [] this->array1;
  128. this->array1 = this->array2;
  129. this->array2 = nullptr;
  130. }
  131. //Delete item from internal array, shrink array size, copy old array to new array(size-1), switch pointer
  132. template <class T>
  133. void dyn_array<T>::deleteItem(int index)
  134. {
  135. //Check to make sure array isn't empty
  136. if(this->getSize() > 0)
  137. {
  138. if(index >= 0 && index <= static_cast<int>(this->size)-1)
  139. {
  140. //If array has more than 1 element perform normal operation, otherwise, just delete and recreate internal array
  141. if(this->getSize() > 1)
  142. {
  143. this->array2 = new T[this->size];
  144.  
  145. //copy array1 to array2 skipping "index"
  146. for(unsigned i=0; i<this->size; i++)
  147. {
  148. if(i!=static_cast<unsigned>(index))
  149. {
  150. if(i>static_cast<unsigned>(index))
  151. this->array2[i-1] = this->array1[i];
  152. else
  153. this->array2[i] = this->array1[i];
  154. }
  155. }
  156.  
  157. //Delete old array and switch
  158. delete [] this->array1;
  159. this->array1 = this->array2;
  160. this->array2 = nullptr;
  161. }
  162. else
  163. {
  164. delete [] this->array1;
  165. this->array1 = new T[1];
  166. }
  167.  
  168. //Make arraysize 1 smaller
  169. this->size--;
  170. }
  171. else
  172. {
  173. std::cout << "Error: index (" << index << ") out of bounds of array!" << std::endl;
  174. exit(0);
  175. }
  176. }
  177. else
  178. {
  179. std::cout << "Error: you can't delete items from an empty array!" << std::endl;
  180. exit(0);
  181. }
  182. }
  183. //Delete all elements in the array and set size back to 0
  184. template <class T>
  185. void dyn_array<T>::clear()
  186. {
  187. delete [] this->array1;
  188. this->size = 0;
  189. this->array1 = new T[this->size+1];
  190. }
  191. //Overloaded [] operator to allow normal array syntax
  192. template <class T>
  193. T& dyn_array<T>::operator[](int index)
  194. {
  195. if(index >= 0 && index <= static_cast<int>(this->size)-1)
  196. return this->array1[index];
  197. else
  198. {
  199. std::cout << "Error: index (" << index << ") out of bounds of array!" << std::endl;
  200. exit(0);
  201. }
  202. }
  203. //Destructor to delete all unused memory
  204. template <class T>
  205. dyn_array<T>::~dyn_array()
  206. {
  207. delete [] this->array1;
  208. this->array1 = nullptr;
  209. }
  210.  
  211. //Function prototype for swapper
  212. template <class T>
  213. void swapper(T&, T&);
  214. //Swaps variables (pointers too)
  215. template <class T>
  216. void swapper(T& item1, T& item2)
  217. {
  218. T temp = item1;
  219. item1 = item2;
  220. item2 = temp;
  221. }
  222.  
  223. //Function prototype for the str_explode
  224. dyn_array<std::string> str_explode(std::string, std::string, bool);
  225. //Splits string into a dynamic string array based on a given delimiter
  226. dyn_array<std::string> str_explode(std::string str, std::string delimiter, bool printErrors=false)
  227. {
  228. //Default result
  229. dyn_array<std::string> result;
  230.  
  231. //Make sure string is bigger than delimiter and it isn't empty
  232. if(str.size() > delimiter.size() && delimiter.size()>0)
  233. {
  234. std::string str_begin = str.substr(0, delimiter.size());
  235. std::string str_end = str.substr((str.size()-delimiter.size()), delimiter.size());
  236.  
  237. //Make sure there aren't any delimiters surrounding the string
  238. if(str_begin!=delimiter && str_end!=delimiter)
  239. {
  240. int duplicatePos = str.find(delimiter+delimiter);
  241. if(duplicatePos > 0)
  242. {
  243. if(printErrors)
  244. std::cout << "Error: double delimiter instance detected" << std::endl;
  245. }
  246. else
  247. {
  248. int matchPos = -1;
  249. unsigned startPos=0;
  250. //Iterate through string at least once
  251. do
  252. {
  253. //Change startPos to end of last found delimiter and push the string in between delimiters into result array
  254. matchPos = str.find(delimiter, startPos);
  255. if(matchPos!=-1)
  256. {
  257. result.push(str.substr(startPos, (matchPos-startPos)));
  258. startPos = matchPos+delimiter.size();
  259. }
  260. else
  261. result.push(str.substr(startPos, (str.size()-startPos)));
  262. }
  263. while(matchPos!=-1);
  264. }
  265. }
  266. else
  267. {
  268. if(printErrors)
  269. std::cout << "Error: string cannot start or end with delimiters" << std::endl;
  270. }
  271. }
  272. else
  273. {
  274. if(printErrors)
  275. std::cout << "Error: string length less than delimiter length and/or delimiter length less than 1" << std::endl;
  276. }
  277.  
  278. return result;
  279. }
  280.  
  281. //Function prototype for removeWhiteSpace
  282. std::string removeWhiteSpace(std::string);
  283. //Returns a string free of spaces, newlines, and tabs
  284. std::string removeWhiteSpace(std::string text)
  285. {
  286. std::string newString;
  287. for(unsigned i=0; i<text.size(); i++)
  288. if(text[i] != ' ' && text[i] != '\n' && text[i] != '\t')
  289. newString += text[i];
  290.  
  291. return newString;
  292. }
  293.  
  294. //Function prototype for allLowerCase
  295. std::string allLowerCase(std::string);
  296. //Returns string with all chars in lowercase
  297. std::string allLowerCase(std::string text)
  298. {
  299. for(unsigned i=0; i<text.size(); i++)
  300. text[i] = tolower(text[i]);
  301.  
  302. return text;
  303. }
  304.  
  305. //Function prototype for allUpperCase
  306. std::string allUpperCase(std::string);
  307. //Returns string with all chars in uppercase
  308. std::string allUpperCase(std::string text)
  309. {
  310. for(unsigned i=0; i<text.size(); i++)
  311. text[i] = toupper(text[i]);
  312.  
  313. return text;
  314. }
  315.  
  316. //Function prototype for capFirstLetter
  317. std::string capFirstLetter(std::string);
  318. //Returns a string with the first letter capitalized
  319. std::string capFirstLetter(std::string text)
  320. {
  321. text[0] = toupper(text[0]);
  322. return text;
  323. }
  324.  
  325. //Function prototype for str_replace
  326. std::string str_replace(std::string, std::string, std::string, bool);
  327. //Returns haystackl string with replacements instead of needles
  328. std::string str_replace(std::string haystack, std::string needle, std::string replacement, bool printErrors=false)
  329. {
  330. //Default result
  331. std::string result;
  332.  
  333. //haystack must be >= needle
  334. if(haystack.size() >= needle.size())
  335. {
  336. int matchPos = -1;
  337. unsigned startPos=0;
  338. do
  339. {
  340. //Change startPos to end of last found needle and concatinate the string in between needles plus the replacement to the result string
  341. matchPos = haystack.find(needle, startPos);
  342. if(matchPos!=-1)
  343. {
  344. result += haystack.substr(startPos, (matchPos-startPos)) + replacement;
  345. startPos = matchPos+needle.size();
  346. }
  347. else
  348. result += haystack.substr(startPos, (haystack.size()-startPos));
  349. }
  350. while(matchPos!=-1);
  351. }
  352. else
  353. {
  354. if(printErrors)
  355. std::cout << "Error: Haystack string size is less than Needle string size" << std::endl;
  356.  
  357. result = haystack;
  358. }
  359.  
  360. return result;
  361. }
  362.  
  363. //Function prototype for formatText
  364. std::string formatText(std::string, std::string);
  365. //Returns a colored string based on color choice
  366. std::string formatText(std::string text, std::string color)
  367. {
  368. /*
  369. reset 0 (everything back to normal)
  370. bold/bright 1 (often a brighter shade of the same colour)
  371. underline 4
  372. inverse 7 (swap foreground and background colours)
  373. bold/bright off 21
  374. underline off 24
  375. inverse off 27
  376. */
  377.  
  378. //Convert color string to lowercase
  379. //No checking for spaces, if that is the other programmer's job
  380. for(unsigned i=0; i<color.size(); i++)
  381. color[i] = std::tolower(color[i]);
  382.  
  383. //Choose formatCode - 0 default
  384. //background starts at 30, foreground starts at 40
  385. //add 60 to either for bright color variant
  386. int formatCode = 0;
  387. if(color == "bold")
  388. formatCode = 1;
  389. if(color == "underline")
  390. formatCode = 4;
  391. if(color == "inverse")
  392. formatCode = 7;
  393. if(color == "black")
  394. formatCode = 30;
  395. if(color == "red")
  396. formatCode = 31;
  397. if(color == "green")
  398. formatCode = 32;
  399. if(color == "yellow")
  400. formatCode = 33;
  401. if(color == "blue")
  402. formatCode = 34;
  403. if(color == "magenta")
  404. formatCode = 35;
  405. if(color == "cyan")
  406. formatCode = 36;
  407. if(color == "white")
  408. formatCode = 37;
  409.  
  410. return "\033["+std::to_string(formatCode)+"m"+text+"\033[0m";
  411. }
  412.  
  413. using namespace std;
  414.  
  415.  
  416. void setCursor(int, int);
  417. void setCursor(int row, int column)
  418. {
  419. cout << "\033["+to_string(row)+";"+to_string(column)+"H";
  420. }
  421.  
  422.  
  423. //Stores input and erases the contents that's converted into whitespace at specific (row,column) coords
  424. class TerminalEraser
  425. {
  426. private:
  427. string eraser;
  428. unsigned _row, _column;
  429. public:
  430. TerminalEraser(unsigned, unsigned);
  431. void resetPos(unsigned, unsigned);
  432. string capture(string);
  433. void erase();
  434. unsigned getSize();
  435. };
  436. TerminalEraser::TerminalEraser(unsigned row, unsigned column) : _row(row), _column(column){}
  437. //Set cursor position to another spot in the terminal
  438. void TerminalEraser::resetPos(unsigned row, unsigned column)
  439. {
  440. this->_row = row;
  441. this->_column = column;
  442. }
  443. //Capture input and repeat it back out
  444. string TerminalEraser::capture(string input)
  445. {
  446. string copy = input;
  447.  
  448. //Strip away possible text format codes
  449. copy = str_replace(copy,"\033[0m","");
  450. copy = str_replace(copy,"\033[1m","");
  451. copy = str_replace(copy,"\033[4m","");
  452. copy = str_replace(copy,"\033[7m","");
  453. copy = str_replace(copy,"\033[30m","");
  454. copy = str_replace(copy,"\033[31m","");
  455. copy = str_replace(copy,"\033[32m","");
  456. copy = str_replace(copy,"\033[33m","");
  457. copy = str_replace(copy,"\033[34m","");
  458. copy = str_replace(copy,"\033[35m","");
  459. copy = str_replace(copy,"\033[36m","");
  460. copy = str_replace(copy,"\033[37m","");
  461.  
  462. //Turn all chars into spaces
  463. for(unsigned i=0; i<copy.size(); i++)
  464. if(copy[i]!='\n' && copy[i]!='\t')
  465. copy[i] = ' ';
  466.  
  467. this->eraser += copy;
  468.  
  469. return input;
  470. }
  471. //Print the eraser
  472. void TerminalEraser::erase()
  473. {
  474. setCursor(this->_row,this->_column);
  475. cout << this->eraser;
  476. setCursor(this->_row,this->_column);
  477. this->eraser = "";
  478. }
  479. //Get size of buffer
  480. unsigned TerminalEraser::getSize()
  481. {
  482. return eraser.size();
  483. }
  484.  
  485.  
  486. enum Spot {Carrier, Battleship, Cruiser, Submarine, Destroyer, Water};
  487.  
  488.  
  489. struct Point
  490. {
  491. int row=0,column=0;
  492. Spot contents = Water;
  493. bool hit=false, miss=false;
  494. };
  495.  
  496.  
  497. class Ship
  498. {
  499. private:
  500. static unsigned lengths[5];
  501. static string names[5];
  502. static string colors[5];
  503. Spot type;
  504. unsigned HP=0;
  505. bool sunk=false;
  506. public:
  507. static unsigned getLength(Spot);
  508. static string getName(Spot);
  509. static string getColor(Spot);
  510. void setup(Spot, unsigned);
  511. Spot getType();
  512. bool isSunk();
  513. void operator--(int);
  514. };
  515. unsigned Ship::lengths[] = {5, 4, 3, 3, 2};
  516. string Ship::names[] = {"carrier","battleship","cruiser","submarine","destroyer"};
  517. string Ship::colors[] = {"red","green","yellow","blue","magenta"};
  518. unsigned Ship::getLength(Spot shipType)
  519. {
  520. return Ship::lengths[shipType];
  521. }
  522. string Ship::getName(Spot shipType)
  523. {
  524. return Ship::names[shipType];
  525. }
  526. string Ship::getColor(Spot shipType)
  527. {
  528. return Ship::colors[shipType];
  529. }
  530. //Fake constructor
  531. void Ship::setup(Spot shipType, unsigned hitPoints)
  532. {
  533. this->type = shipType;
  534. this->HP = hitPoints;
  535. }
  536. //Get the stored type of shipe
  537. Spot Ship::getType()
  538. {
  539. return this->type;
  540. }
  541. //Has the ship been sunk?
  542. bool Ship::isSunk()
  543. {
  544. return this->sunk;
  545. }
  546. //Change sunk to true if HP drops to 0
  547. void Ship::operator--(int n)
  548. {
  549. this->HP--;
  550. if(HP==0)
  551. this->sunk = true;
  552. }
  553.  
  554.  
  555. struct PlayerData
  556. {
  557. dyn_array<dyn_array<Point>> board_player;
  558. dyn_array<dyn_array<Point>> board_enemy;
  559. dyn_array<Ship> ships_player;
  560. dyn_array<Ship> ships_enemy;
  561. string playerName;
  562. string playerType;
  563. };
  564.  
  565.  
  566. string boardToString(dyn_array<dyn_array<Point>>);
  567. string boardToString(dyn_array<dyn_array<Point>> board)
  568. {
  569. string output="";
  570.  
  571. output += " A B C D E F G H I J\n";
  572. for(unsigned row=0; row<10; row++)
  573. {
  574. if(row<9)
  575. output += " " + to_string(row+1);
  576. else
  577. output += to_string(row+1);
  578.  
  579. for(unsigned column=0; column<10; column++)
  580. {
  581. if(board[row][column].contents != Water)
  582. {
  583. if(board[row][column].hit)
  584. output += formatText(" *", Ship::getColor(board[row][column].contents));
  585. else
  586. output += formatText(" @", Ship::getColor(board[row][column].contents));
  587. }
  588. else
  589. {
  590. if(board[row][column].miss)
  591. output += " #";
  592. else
  593. output += " ";
  594. }
  595. }
  596. output += "\n";
  597. }
  598.  
  599. return output;
  600. }
  601.  
  602. //TODO add recordMove()
  603. //Singleton class prototype for Game
  604. class Game
  605. {
  606. private:
  607. //Lock down constructor
  608. Game(){};
  609. static Game* _instance;
  610. unsigned numPlayers = 0;
  611. bool active=true;
  612. dyn_array<string> messages;
  613. dyn_array<dyn_array<Point>> cheat_board;
  614. void setupPlayerHuman(PlayerData&);
  615. void setupPlayerComputer(PlayerData&);
  616. Point makeMoveHuman(PlayerData&);
  617. Point makeMoveComputer(PlayerData&);
  618. public:
  619. static Game* getInstance();
  620. bool gameActive();
  621. void cheatBoard(PlayerData&);
  622. void setupPlayer(PlayerData&);
  623. void makeMove(PlayerData&,PlayerData&);
  624. void pushMessage(string);
  625. string pullMessage();
  626. };
  627. Game* Game::_instance = nullptr;
  628. Game* Game::getInstance()
  629. {
  630. //Ensure only 1 active instance
  631. if (_instance == nullptr)
  632. _instance = new Game();
  633.  
  634. return _instance;
  635. }
  636. bool Game::gameActive()
  637. {
  638. return this->active;
  639. }
  640. void Game::cheatBoard(PlayerData& enemyPlayer)
  641. {
  642. this->cheat_board = enemyPlayer.board_player;
  643. }
  644. void Game::setupPlayerHuman(PlayerData& player)
  645. {
  646. //Controllers that handle printing, and erasing of the screen
  647. TerminalEraser instructionPrinter(1,1), inputPrinter(1,1), boardPrinter(6,1), shipListPrinter(22,1), errorPrinter(18,1), shipTitle(21,1), commandTitle(19,1);
  648.  
  649. string playerInput;
  650. dyn_array<Ship> boardList, shipList;
  651. shipList = player.ships_player;
  652.  
  653. //Get player's name
  654. cout << inputPrinter.capture("Alright Player"+to_string(this->numPlayers)+", let's get your name and setup the board!\nInput: ");
  655. getline(cin, playerInput);
  656. inputPrinter.capture(playerInput+"\n");
  657. player.playerName = playerInput;
  658.  
  659. //Make sure player is ready
  660. cout << inputPrinter.capture("Howdy " + player.playerName + "! Let's get started!\n");
  661. cout << inputPrinter.capture(" (make sure no one is looking and enter \"ready\" to continue.)\n");
  662. do
  663. {
  664. cout << inputPrinter.capture(" status: ");
  665. getline(cin, playerInput);
  666. inputPrinter.capture(playerInput+"\n");
  667.  
  668. playerInput = removeWhiteSpace(playerInput);
  669. playerInput = allLowerCase(playerInput);
  670.  
  671. if(playerInput!="ready")
  672. cout << inputPrinter.capture("\nOk, come on... all you need to do is type \"ready\". Try again...\n");
  673. }
  674. while(playerInput!="ready");
  675. //Erase everything above
  676. inputPrinter.erase();
  677.  
  678. //Print out instructions
  679. cout << instructionPrinter.capture("Place EACH ship on the board. (ships can be replaced multiple times)\n");
  680. cout << instructionPrinter.capture("Enter \"confirm\" once you're ready to move on.\n");
  681. cout << instructionPrinter.capture("Format: place getName x(A-J),y(1-10),rotation(0-1)\n");
  682. cout << instructionPrinter.capture("Ex: place Battleship A,3,1\n");
  683.  
  684. //Print out the titles
  685. setCursor(19,1);
  686. cout << commandTitle.capture("Command: ");
  687. setCursor(21,1);
  688. cout << shipTitle.capture("Ships:");
  689.  
  690. setCursor(18,1);
  691. cout << errorPrinter.capture("Enter a command...");
  692.  
  693. //Reset the inputPrinter to just after "Command: "
  694. inputPrinter.resetPos(19,10);
  695.  
  696. //Main loop for placing ship logic
  697. while(!(shipList.getSize() == 0 && playerInput=="confirm"))
  698. {
  699. //Print current board layout
  700. boardPrinter.erase();
  701. cout << boardPrinter.capture(boardToString(player.board_player)+"\n\n");
  702.  
  703. //Print ships that have yet to be placed
  704. shipListPrinter.erase();
  705. for(unsigned i=0; i<shipList.getSize(); i++)
  706. cout << shipListPrinter.capture(formatText(capFirstLetter(Ship::getName(shipList[i].getType())), Ship::getColor(shipList[i].getType()))+"\n");
  707. if(shipList.getSize() == 0)
  708. shipTitle.erase();
  709.  
  710. //Accept player commands (erase last command)
  711. setCursor(19,10);
  712. getline(cin, playerInput);
  713. inputPrinter.capture(playerInput+"\n");
  714. inputPrinter.erase();
  715.  
  716. //Main loop for trying to place ships + input validation
  717. playerInput = allLowerCase(playerInput);
  718. if(playerInput!="confirm")
  719. {
  720. dyn_array<string> args = str_explode(playerInput, " ");
  721. if(args.getSize() == 3)
  722. {
  723. if(args[0] == "place")
  724. {
  725. int shipListMatch=-1, boardListMatch=-1;
  726. Ship currentShip;
  727.  
  728. //Search shipList for matching getName
  729. for(unsigned i=0; i<shipList.getSize(); i++)
  730. {
  731. if(args[1] == Ship::getName(shipList[i].getType()))
  732. {
  733. shipListMatch = i;
  734. currentShip = shipList[i];
  735. }
  736. }
  737. //Search boardList for matching getName
  738. for(unsigned i=0; i<boardList.getSize(); i++)
  739. {
  740. if(args[1] == Ship::getName(boardList[i].getType()))
  741. {
  742. boardListMatch = i;
  743. currentShip = boardList[i];
  744. }
  745. }
  746.  
  747. if(shipListMatch>=0 || boardListMatch>=0)
  748. {
  749. int inputColumn=-1, InputRow=-1, inputRotation=-1;
  750.  
  751. //Gather coordinates and rotation
  752. dyn_array<string> coords = str_explode(args[2], ",");
  753. if(coords.getSize() == 3)
  754. {
  755. string arg1 = coords[0];
  756. string arg2 = coords[1];
  757. string arg3 = coords[2];
  758.  
  759. //Input validation for coordintes
  760. if(arg1.size() == 1 && static_cast<int>(arg1[0])>=97 && static_cast<int>(arg1[0])<=106)
  761. {
  762. inputColumn = static_cast<int>(arg1[0])-97;
  763.  
  764. if(isInt(arg2, true))
  765. {
  766. int num = stoi(arg2);
  767. if(num>= 1 && num <=10)
  768. {
  769. InputRow = num-1;
  770. if(arg3.size() == 1 && (arg3[0] == '1' || arg3[0] == '0'))
  771. {
  772. inputRotation = stoi(arg3);
  773.  
  774. unsigned shipLength = Ship::getLength(currentShip.getType());
  775. bool placement = true, shipFit=true;
  776.  
  777. if(inputRotation==0)
  778. shipFit = (shipLength+inputColumn)<=10;
  779. else
  780. shipFit = (shipLength+InputRow)<=10;
  781.  
  782. if(shipFit)
  783. {
  784. for(unsigned i=0; i<shipLength; i++)
  785. {
  786. if(inputRotation==0)
  787. {
  788. if(player.board_player[InputRow][inputColumn+i].contents != Water)
  789. placement = false;
  790. }
  791. else
  792. {
  793. if(player.board_player[InputRow+i][inputColumn].contents != Water)
  794. placement = false;
  795. }
  796. }
  797.  
  798. //Place the ship on the board once a valid spot is found
  799. if(placement)
  800. {
  801. if(shipListMatch>=0)
  802. {
  803. boardList.push(currentShip);
  804. shipList.deleteItem(shipListMatch);
  805. }
  806. //Erase the ship from the board if it exists already
  807. if(boardListMatch>=0)
  808. {
  809. for(unsigned row=0; row<10; row++)
  810. {
  811. for(unsigned column=0; column<10; column++)
  812. {
  813. if(player.board_player[row][column].contents == boardList[boardListMatch].getType())
  814. player.board_player[row][column].contents = Water;
  815. }
  816. }
  817. }
  818. //Place the ship on the board spot by spot
  819. for(unsigned i=0; i<shipLength; i++)
  820. {
  821. if(inputRotation==0)
  822. player.board_player[InputRow][inputColumn+i].contents = currentShip.getType();
  823. else
  824. player.board_player[InputRow+i][inputColumn].contents = currentShip.getType();
  825. }
  826.  
  827. errorPrinter.erase();
  828. cout << errorPrinter.capture("Placed: "+formatText(capFirstLetter(Ship::getName(currentShip.getType())), Ship::getColor(currentShip.getType())) + " @ ("+string(1,char(inputColumn+65))+","+to_string(InputRow+1)+","+to_string(inputRotation)+")");
  829. }
  830. else
  831. {
  832. errorPrinter.erase();
  833. cout << errorPrinter.capture("There seem's to be another ship in the way of your placement.");
  834. }
  835. }
  836. else
  837. {
  838. errorPrinter.erase();
  839. cout << errorPrinter.capture("You ship won't fit on the board using the coordinates: ("+to_string(inputColumn)+","+to_string(InputRow+1)+")");
  840. }
  841. }
  842. else
  843. {
  844. errorPrinter.erase();
  845. cout << errorPrinter.capture("Ship coordinate \"rotation\" must be either 0 or 1.");
  846. }
  847. }
  848. else
  849. {
  850. errorPrinter.erase();
  851. cout << errorPrinter.capture("Ship coordinate \"row\" must be 1-10.");
  852. }
  853. }
  854. else
  855. {
  856. errorPrinter.erase();
  857. cout << errorPrinter.capture("Ship coordinate \"row\" must be a number.");
  858. }
  859. }
  860. else
  861. {
  862. errorPrinter.erase();
  863. cout << errorPrinter.capture("Ship coordinate \"column\" must be A-J.");
  864. }
  865. }
  866. else
  867. {
  868. errorPrinter.erase();
  869. cout << errorPrinter.capture("Ship coordinate scheme requires 3 comma seperated arguments.");
  870. }
  871. }
  872. else
  873. {
  874. errorPrinter.erase();
  875. cout << errorPrinter.capture("That ship doesn't exist.");
  876. }
  877. }
  878. else
  879. {
  880. errorPrinter.erase();
  881. cout << errorPrinter.capture("Invalid first argument. 1st arg must either be \"place\" or \"confirm\".");
  882. }
  883. }
  884. else
  885. {
  886. errorPrinter.erase();
  887. cout << errorPrinter.capture("Invalid command.");
  888. }
  889. }
  890. else
  891. {
  892. if(shipList.getSize()>0)
  893. {
  894. errorPrinter.erase();
  895. cout << errorPrinter.capture("You still have ships left to place!");
  896. }
  897. }
  898. }
  899.  
  900. //Clear the screen of clutter
  901. instructionPrinter.erase();
  902. commandTitle.erase();
  903. shipTitle.erase();
  904. boardPrinter.erase();
  905. errorPrinter.erase();
  906. shipListPrinter.erase();
  907. setCursor(1,1);
  908. }
  909. void Game::setupPlayerComputer(PlayerData& player)
  910. {
  911. //Set the computer's name
  912. player.playerName = "Computer"+to_string(this->numPlayers);
  913.  
  914. for(int i=0; i<5; i++)
  915. {
  916. bool placement=false;
  917. Spot currentShip = static_cast<Spot>(i);
  918. unsigned shipLength = Ship::getLength(currentShip);
  919. int tryRow, tryColumn, rotation;
  920.  
  921. //Loop until valid ship spot is found (pick point, rotate vert/horiz, pick new point, repeat)
  922. while(!placement)
  923. {
  924. //make placement true unless proven false
  925. placement = true;
  926. //randomly choose rotation and coords (max board size - length of ship)
  927. tryRow = getRandom(0, (10-shipLength));
  928. tryColumn = getRandom(0, (10-shipLength));
  929. for(unsigned j=0; j<shipLength; j++)
  930. {
  931. if(rotation==0)
  932. {
  933. if(player.board_player[tryRow][tryColumn+j].contents != Water)
  934. placement = false;
  935. }
  936. else
  937. {
  938. if(player.board_player[tryRow+j][tryColumn].contents != Water)
  939. placement = false;
  940. }
  941. }
  942. if(!placement)
  943. {
  944. //reset placement and invert rotation
  945. placement = true;
  946. if(rotation==0)
  947. rotation = 1;
  948. else
  949. rotation = 0;
  950.  
  951. for(unsigned j=0; j<shipLength; j++)
  952. {
  953. if(rotation==0)
  954. {
  955. if(player.board_player[tryRow][tryColumn+j].contents != Water)
  956. placement = false;
  957. }
  958. else
  959. {
  960. if(player.board_player[tryRow+j][tryColumn].contents != Water)
  961. placement = false;
  962. }
  963. }
  964. }
  965. }
  966.  
  967. //Place the ship on the board once a valid spot is found
  968. for(unsigned j=0; j<shipLength; j++)
  969. {
  970. if(rotation==0)
  971. player.board_player[tryRow][tryColumn+j].contents = currentShip;
  972. else
  973. player.board_player[tryRow+j][tryColumn].contents = currentShip;
  974. }
  975. }
  976. }
  977. void Game::setupPlayer(PlayerData& player)
  978. {
  979. this->numPlayers++;
  980.  
  981. //Fill both boards with a 10x10 grid of Point objects
  982. for(unsigned row=0; row<10; row++)
  983. {
  984. player.board_player.push(dyn_array<Point>());
  985. player.board_enemy.push(dyn_array<Point>());
  986. for(unsigned column=0; column<10; column++)
  987. {
  988. player.board_player[row].push(Point());
  989. player.board_player[row][column].row=row;
  990. player.board_player[row][column].column=column;
  991.  
  992. player.board_enemy[row].push(Point());
  993. player.board_enemy[row][column].row=row;
  994. player.board_enemy[row][column].column=column;
  995. }
  996. }
  997.  
  998. //Instantiate and setup ship objects (type, hitpoints)
  999. for(unsigned i=0; i<5; i++)
  1000. player.ships_player.push(Ship());
  1001. player.ships_player[Carrier].setup(Carrier, 5);
  1002. player.ships_player[Battleship].setup(Battleship, 4);
  1003. player.ships_player[Cruiser].setup(Cruiser, 3);
  1004. player.ships_player[Submarine].setup(Submarine, 3);
  1005. player.ships_player[Destroyer].setup(Destroyer, 2);
  1006. player.ships_enemy = player.ships_player;
  1007.  
  1008. if(player.playerType == "human")
  1009. {
  1010. this->setupPlayerHuman(player);
  1011. }
  1012. if(player.playerType == "computer")
  1013. this->setupPlayerComputer(player);
  1014. }
  1015. Point Game::makeMoveHuman(PlayerData& p1)
  1016. {
  1017. //TODO add documentation
  1018. //TODO allow quit
  1019.  
  1020. TerminalEraser inputPrinter(1,1), instructionPrinter(27,1), boardPlayerTitle(1,1), boardPlayerPrinter(2,1), boardEnemyTitle(14,1), boardEnemyPrinter(15,1), errorPrinter(31,1);
  1021. string playerInput;
  1022.  
  1023. //Print out stored messages
  1024. string message = this->pullMessage();
  1025. if(message!="")
  1026. {
  1027. while(message!="")
  1028. {
  1029. cout << inputPrinter.capture(message+"\n");
  1030. message = this->pullMessage();
  1031. }
  1032. cout << inputPrinter.capture("\n");
  1033. }
  1034.  
  1035. //Make sure player is ready
  1036. cout << inputPrinter.capture("Alright " + p1.playerName + ", it's your turn to take a shot!\n (make sure no one is looking and enter 'ready' to continue.)\n");
  1037. do
  1038. {
  1039. cout << inputPrinter.capture(" status: ");
  1040. getline(cin, playerInput);
  1041. inputPrinter.capture(playerInput+"\n");
  1042.  
  1043. playerInput = removeWhiteSpace(playerInput);
  1044. playerInput = allLowerCase(playerInput);
  1045.  
  1046. if(playerInput!="ready")
  1047. cout << inputPrinter.capture("\nOk, come on... all you need to do is type \"ready\". Try again.\n");
  1048. }
  1049. while(playerInput!="ready");
  1050. //Erase everything above
  1051. inputPrinter.erase();
  1052.  
  1053. instructionPrinter.erase();
  1054. cout << instructionPrinter.capture("Look at the enemy's board, and select a point to shoot.("+formatText(formatText("+", "cyan"), "bold")+")\n");
  1055. cout << instructionPrinter.capture("Format: letter,number (Ex: A,3)\n");
  1056. cout << instructionPrinter.capture("You can replace coordinates, or enter \"confirm\" to continue.\n");
  1057.  
  1058. boardPlayerTitle.erase();
  1059. cout << boardPlayerTitle.capture(formatText(formatText("====== Your Board =======\n", "green"), "bold"));
  1060. boardEnemyTitle.erase();
  1061. cout << boardEnemyTitle.capture(formatText(formatText("===== Enemy's Board =====\n", "red"), "bold"));
  1062. errorPrinter.erase();
  1063. cout << errorPrinter.capture("Eneter a coordinate...");
  1064. inputPrinter.resetPos(32,1);
  1065.  
  1066. int shotRow=-1, shotColumn=-1, testRow, testColumn;
  1067. dyn_array<string> args;
  1068. while(playerInput!="confirm")
  1069. {
  1070. boardPlayerPrinter.erase();
  1071. cout << boardPlayerPrinter.capture(boardToString(p1.board_player));
  1072.  
  1073. boardEnemyPrinter.erase();
  1074. if(playerInput=="cheat")
  1075. {
  1076. cout << boardEnemyPrinter.capture(boardToString(this->cheat_board));
  1077. errorPrinter.erase();
  1078. cout << errorPrinter.capture("You cheated!!!");
  1079. }
  1080. else
  1081. cout << boardEnemyPrinter.capture(boardToString(p1.board_enemy));
  1082.  
  1083. if(shotRow!=-1 && shotColumn!=-1)
  1084. {
  1085. setCursor((shotRow+1)+15,((shotColumn+1)*2)+2);
  1086. cout << formatText(formatText("+", "cyan"), "bold");
  1087. }
  1088.  
  1089. inputPrinter.erase();
  1090. cout << inputPrinter.capture("Command: ");
  1091. getline(cin, playerInput);
  1092. inputPrinter.capture(playerInput+"\n");
  1093.  
  1094. playerInput = removeWhiteSpace(playerInput);
  1095. playerInput = allLowerCase(playerInput);
  1096.  
  1097. testColumn=-1;
  1098. testRow=-1;
  1099. if(playerInput!="confirm")
  1100. {
  1101. if(playerInput!="cheat")
  1102. {
  1103. args = str_explode(playerInput, ",");
  1104. if(args.getSize() == 2)
  1105. {
  1106. if(args[0].size() == 1 && static_cast<int>(args[0][0])>=97 && static_cast<int>(args[0][0])<=106)
  1107. {
  1108. testColumn = static_cast<int>(args[0][0])-97;
  1109. if(isInt(args[1], true))
  1110. {
  1111. testRow = stoi(args[1]);
  1112. if(testRow>=1 && testRow<=10)
  1113. {
  1114. if(!p1.board_enemy[testRow-1][testColumn].hit && !p1.board_enemy[testRow-1][testColumn].miss)
  1115. {
  1116. shotColumn = testColumn;
  1117. shotRow = testRow-1;
  1118. errorPrinter.erase();
  1119. cout << errorPrinter.capture("Nice shot! You chose: "+string(1,char(shotColumn+65))+","+to_string(shotRow+1));
  1120. }
  1121. else
  1122. {
  1123. errorPrinter.erase();
  1124. cout << errorPrinter.capture("That spot has already been fired upon.");
  1125. }
  1126. }
  1127. else
  1128. {
  1129. errorPrinter.erase();
  1130. cout << errorPrinter.capture("The second coordinate must be 1-10.");
  1131. }
  1132. }
  1133. else
  1134. {
  1135. errorPrinter.erase();
  1136. cout << errorPrinter.capture("The second coordinate must be a non-zero integer.");
  1137. }
  1138. }
  1139. else
  1140. {
  1141. errorPrinter.erase();
  1142. cout << errorPrinter.capture("The first coordinate must be A-J.");
  1143. }
  1144. }
  1145. else
  1146. {
  1147. errorPrinter.erase();
  1148. cout << errorPrinter.capture("Invalid command. Either comma seperated coordinates or \"confirm\".");
  1149. }
  1150. }
  1151. }
  1152. else
  1153. {
  1154. if(shotRow==-1 && shotColumn==-1)
  1155. {
  1156. playerInput="";
  1157. errorPrinter.erase();
  1158. cout << errorPrinter.capture("You haven't selected a valid point yet!");
  1159. }
  1160. }
  1161. }
  1162.  
  1163. //Clear the terminal
  1164. inputPrinter.erase();
  1165. instructionPrinter.erase();
  1166. boardEnemyTitle.erase();
  1167. boardEnemyPrinter.erase();
  1168. boardPlayerTitle.erase();
  1169. boardPlayerPrinter.erase();
  1170. errorPrinter.erase();
  1171. setCursor(1,1);
  1172.  
  1173. //Return selected point by value
  1174. return p1.board_enemy[shotRow][shotColumn];
  1175. }
  1176. Point Game::makeMoveComputer(PlayerData& p1)
  1177. {
  1178. //make this the LAST thing you work on!!!
  1179. //TODO make sure it hasn't been fired on before
  1180. //if you find a ship, keep track of what ship it is, and what size it is
  1181. //then keep firing on that ship until it is destroyed
  1182. //then search for aother random point
  1183. Point pt;
  1184. pt.row = getRandom(0,2);
  1185. pt.column = getRandom(0,2);
  1186.  
  1187. return pt;
  1188. }
  1189. void Game::makeMove(PlayerData& p1, PlayerData& p2)
  1190. {
  1191. Point shotAttempt, shotResult;
  1192.  
  1193. if(p1.playerType == "human")
  1194. shotAttempt = this->makeMoveHuman(p1);
  1195. if(p1.playerType == "computer")
  1196. shotAttempt = this->makeMoveComputer(p1);
  1197.  
  1198. //Grab targeted point
  1199. shotResult = p2.board_player[shotAttempt.row][shotAttempt.column];
  1200.  
  1201. if(shotResult.contents != Water)
  1202. {
  1203. shotResult.hit = true;
  1204. p2.ships_player[shotResult.contents]--;
  1205. p1.ships_enemy[shotResult.contents]--;
  1206. }
  1207. else
  1208. {
  1209. shotResult.miss = true;
  1210. }
  1211.  
  1212. p1.board_enemy[shotResult.row][shotResult.column] = shotResult;
  1213. p2.board_player[shotResult.row][shotResult.column] = shotResult;
  1214.  
  1215. if(p1.playerType=="human")
  1216. {
  1217. if(shotResult.hit)
  1218. {
  1219. this->pushMessage("You hit " + p2.playerName + "'s " + capFirstLetter(Ship::getName(shotResult.contents)) +"!");
  1220. if(p2.ships_player[shotResult.contents].isSunk())
  1221. this->pushMessage("You just sunk " + p2.playerName + "'s " + capFirstLetter(Ship::getName(shotResult.contents)) + "!!!");
  1222. }
  1223. else
  1224. this->pushMessage("You missed!");
  1225.  
  1226. //Print out stored messages
  1227. TerminalEraser screenPrint(1,1);
  1228. string output = this->pullMessage();
  1229. while(output!="")
  1230. {
  1231. cout << screenPrint.capture(output+"\n");
  1232. output = this->pullMessage();
  1233. }
  1234.  
  1235. //Wait for player to enter "continue"
  1236. cout << screenPrint.capture("\nPlease enter \"continue\" to proceeed...\n");
  1237. do
  1238. {
  1239. cout << screenPrint.capture(" Input: ");
  1240. getline(cin, output);
  1241. screenPrint.capture(output+"\n");
  1242.  
  1243. output = removeWhiteSpace(output);
  1244. output = allLowerCase(output);
  1245.  
  1246. if(output!="continue")
  1247. cout << screenPrint.capture("\n Ok, come on... all you need to do is type \"continue\". Try again.\n");
  1248. }
  1249. while(output!="continue");
  1250. //Erase everything above
  1251. screenPrint.erase();
  1252. }
  1253. }
  1254. void Game::pushMessage(string text)
  1255. {
  1256. this->messages.push(text);
  1257. }
  1258. string Game::pullMessage()
  1259. {
  1260. string text="";
  1261.  
  1262. if(this->messages.getSize() > 0)
  1263. {
  1264. text = messages[0];
  1265. messages.deleteItem(0);
  1266. }
  1267.  
  1268. return text;
  1269. }
  1270.  
  1271. /*
  1272. bool Game::allSunk()
  1273. {
  1274. if(this->ships[Carrier].sunk && this->ships[Battleship].sunk && this->ships[Cruiser].sunk && this->ships[Submarine].sunk && this->ships[Destroyer].sunk)
  1275. return true;
  1276. else
  1277. return false;
  1278. }
  1279. */
  1280.  
  1281. int main()
  1282. {
  1283. system("clear");
  1284.  
  1285. TerminalEraser mainOutput(1,1);
  1286.  
  1287. cout << mainOutput.capture("Welcome to the game of Battleship!\n");
  1288.  
  1289. dyn_array<PlayerData> players;
  1290. string input;
  1291.  
  1292. for(unsigned i=0; i<2; i++)
  1293. {
  1294. cout << mainOutput.capture("\nWill Player" + to_string(i+1) + " be human? (yes/no)\nChoice: ");
  1295.  
  1296. do
  1297. {
  1298. getline(cin, input);
  1299. mainOutput.capture(input+"\n");
  1300.  
  1301. input = removeWhiteSpace(input);
  1302. for(unsigned i=0; i<input.size(); i++)
  1303. input[i] = tolower(input[i]);
  1304.  
  1305. if(input != "yes" && input!="no")
  1306. cout << mainOutput.capture("\nThis is really simple... either 'yes' or 'no'. Try again!\nChoice: ");
  1307. else
  1308. {
  1309. if(input=="yes")
  1310. {
  1311. players.push(PlayerData());
  1312. players[i].playerType = "human";
  1313. }
  1314. else
  1315. {
  1316. players.push(PlayerData());
  1317. players[i].playerType = "computer";
  1318. }
  1319. }
  1320. }
  1321. while(input != "yes" && input!="no");
  1322. }
  1323.  
  1324. mainOutput.erase();
  1325.  
  1326. //get players ready
  1327. for(unsigned i=0; i<2; i++)
  1328. Game::getInstance()->setupPlayer(players[i]);
  1329.  
  1330. PlayerData& currentPlayer = players[0];
  1331. PlayerData& otherPlayer = players[1];
  1332. bool keepPlaying = true;
  1333.  
  1334. //currentPlayer->allSunk()
  1335. while(keepPlaying)
  1336. {
  1337. //grab the other player's board
  1338. Game::getInstance()->cheatBoard(otherPlayer);
  1339.  
  1340. if(currentPlayer.playerType == "human")
  1341. {
  1342. Game::getInstance()->pushMessage("You have 5 moves left!");
  1343. Game::getInstance()->pushMessage("Also, I'm super hungry and tired. Can you help?");
  1344. }
  1345. Game::getInstance()->makeMove(currentPlayer, otherPlayer);
  1346.  
  1347. mainOutput.erase();
  1348.  
  1349. swapper(currentPlayer, otherPlayer);
  1350. }
  1351.  
  1352. return 0;
  1353. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement