Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <iostream>
- #include <string> //string + string operations
- #include <cstdlib> //srand() and system("clear")
- #include <ctime> //time(0)
- #include <memory>
- //Return a random number between min and max
- int getRandom(int min, int max)
- {
- //Set initial state of seeded for rand()
- static bool seeded = false;
- if(!seeded)
- {
- srand(time(0));
- seeded = true;
- }
- return (rand()%(max-min+1)) + min;
- }
- //Check to see if a string could be an integer
- bool isInt(std::string number, bool requireUnsigned=false)
- {
- bool result = false;
- if(number.size() > 0)
- {
- result = true;
- unsigned startPos = 0;
- //If the number starts with a hyphen, start the loop counter @[1]
- if(number[0] == '-')
- startPos = 1;
- //Iterate through the string and set to false if input isn't an integer
- for(unsigned i = startPos; i<number.size(); i++)
- if(!isdigit(number[i]))
- result = false;
- //Check for above zero when required
- if(requireUnsigned && number[0] == '-')
- result = false;
- }
- return result;
- }
- //Dynamically resizing arrays that accept any data type
- //Works with pointers, but it's the programmer's responsibility to delete memory
- template <class T>
- class dyn_array
- {
- private:
- unsigned size;
- T* array1;
- T* array2;
- public:
- unsigned getSize();
- dyn_array();
- dyn_array(const dyn_array&);
- dyn_array& operator=(const dyn_array&);
- void push(T);
- void deleteItem(int);
- void clear();
- T& operator[](int);
- ~dyn_array();
- };
- //Return the current size of the array
- template <class T>
- unsigned dyn_array<T>::getSize()
- {
- return this->size;
- }
- //Constructor that sets the initial array size and dynamically allocates an internal array
- template <class T>
- dyn_array<T>::dyn_array()
- {
- this->size = 0;
- this->array1 = new T[this->size+1];
- this->array2 = nullptr;
- }
- //Copy constructor to copy size, create a new array and copy its contents
- template <class T>
- dyn_array<T>::dyn_array(const dyn_array& obj)
- {
- this->size = obj.size;
- this->array1 = new T[this->size+1];
- for(unsigned i=0; i<this->size; i++)
- this->array1[i] = obj.array1[i];
- this->array2 = nullptr;
- }
- //Overloaded = operator for copying
- template <class T>
- dyn_array<T>& dyn_array<T>::operator=(const dyn_array& obj)
- {
- //Check if it tries to copy itself
- if(this != &obj)
- {
- //Delete old array
- delete [] this->array1;
- this->size = obj.size;
- this->array1 = new T[this->size+1];
- for(unsigned i=0; i<this->size; i++)
- this->array1[i] = obj.array1[i];
- this->array2 = nullptr;
- }
- return *this;
- }
- //Allow pushing new elements to the array
- template <class T>
- void dyn_array<T>::push(T data)
- {
- //Insert new element into the empty spot
- this->array1[this->size] = data;
- this->size++;
- this->array2 = new T[this->size+1];
- //copy to array2 + 1
- for(unsigned i=0; i<this->size; i++)
- this->array2[i] = this->array1[i];
- //Delete old array and switch
- delete [] this->array1;
- this->array1 = this->array2;
- this->array2 = nullptr;
- }
- //Delete item from internal array, shrink array size, copy old array to new array(size-1), switch pointer
- template <class T>
- void dyn_array<T>::deleteItem(int index)
- {
- //Check to make sure array isn't empty
- if(this->getSize() > 0)
- {
- if(index >= 0 && index <= static_cast<int>(this->size)-1)
- {
- //If array has more than 1 element perform normal operation, otherwise, just delete and recreate internal array
- if(this->getSize() > 1)
- {
- this->array2 = new T[this->size];
- //copy array1 to array2 skipping "index"
- for(unsigned i=0; i<this->size; i++)
- {
- if(i!=static_cast<unsigned>(index))
- {
- if(i>static_cast<unsigned>(index))
- this->array2[i-1] = this->array1[i];
- else
- this->array2[i] = this->array1[i];
- }
- }
- //Delete old array and switch
- delete [] this->array1;
- this->array1 = this->array2;
- this->array2 = nullptr;
- }
- else
- {
- delete [] this->array1;
- this->array1 = new T[1];
- }
- //Make arraysize 1 smaller
- this->size--;
- }
- else
- {
- std::cout << "Error: index (" << index << ") out of bounds of array!" << std::endl;
- exit(0);
- }
- }
- else
- {
- std::cout << "Error: you can't delete items from an empty array!" << std::endl;
- exit(0);
- }
- }
- //Delete all elements in the array and set size back to 0
- template <class T>
- void dyn_array<T>::clear()
- {
- delete [] this->array1;
- this->size = 0;
- this->array1 = new T[this->size+1];
- }
- //Overloaded [] operator to allow normal array syntax
- template <class T>
- T& dyn_array<T>::operator[](int index)
- {
- if(index >= 0 && index <= static_cast<int>(this->size)-1)
- return this->array1[index];
- else
- {
- std::cout << "Error: index (" << index << ") out of bounds of array!" << std::endl;
- exit(0);
- }
- }
- //Destructor to delete all unused memory
- template <class T>
- dyn_array<T>::~dyn_array()
- {
- delete [] this->array1;
- this->array1 = nullptr;
- }
- //Function prototype for swapper
- template <class T>
- void swapper(T&, T&);
- //Swaps variables (pointers too)
- template <class T>
- void swapper(T& item1, T& item2)
- {
- T temp = item1;
- item1 = item2;
- item2 = temp;
- }
- //Function prototype for the str_explode
- dyn_array<std::string> str_explode(std::string, std::string, bool);
- //Splits string into a dynamic string array based on a given delimiter
- dyn_array<std::string> str_explode(std::string str, std::string delimiter, bool printErrors=false)
- {
- //Default result
- dyn_array<std::string> result;
- //Make sure string is bigger than delimiter and it isn't empty
- if(str.size() > delimiter.size() && delimiter.size()>0)
- {
- std::string str_begin = str.substr(0, delimiter.size());
- std::string str_end = str.substr((str.size()-delimiter.size()), delimiter.size());
- //Make sure there aren't any delimiters surrounding the string
- if(str_begin!=delimiter && str_end!=delimiter)
- {
- int duplicatePos = str.find(delimiter+delimiter);
- if(duplicatePos > 0)
- {
- if(printErrors)
- std::cout << "Error: double delimiter instance detected" << std::endl;
- }
- else
- {
- int matchPos = -1;
- unsigned startPos=0;
- //Iterate through string at least once
- do
- {
- //Change startPos to end of last found delimiter and push the string in between delimiters into result array
- matchPos = str.find(delimiter, startPos);
- if(matchPos!=-1)
- {
- result.push(str.substr(startPos, (matchPos-startPos)));
- startPos = matchPos+delimiter.size();
- }
- else
- result.push(str.substr(startPos, (str.size()-startPos)));
- }
- while(matchPos!=-1);
- }
- }
- else
- {
- if(printErrors)
- std::cout << "Error: string cannot start or end with delimiters" << std::endl;
- }
- }
- else
- {
- if(printErrors)
- std::cout << "Error: string length less than delimiter length and/or delimiter length less than 1" << std::endl;
- }
- return result;
- }
- //Function prototype for removeWhiteSpace
- std::string removeWhiteSpace(std::string);
- //Returns a string free of spaces, newlines, and tabs
- std::string removeWhiteSpace(std::string text)
- {
- std::string newString;
- for(unsigned i=0; i<text.size(); i++)
- if(text[i] != ' ' && text[i] != '\n' && text[i] != '\t')
- newString += text[i];
- return newString;
- }
- //Function prototype for allLowerCase
- std::string allLowerCase(std::string);
- //Returns string with all chars in lowercase
- std::string allLowerCase(std::string text)
- {
- for(unsigned i=0; i<text.size(); i++)
- text[i] = tolower(text[i]);
- return text;
- }
- //Function prototype for allUpperCase
- std::string allUpperCase(std::string);
- //Returns string with all chars in uppercase
- std::string allUpperCase(std::string text)
- {
- for(unsigned i=0; i<text.size(); i++)
- text[i] = toupper(text[i]);
- return text;
- }
- //Function prototype for capFirstLetter
- std::string capFirstLetter(std::string);
- //Returns a string with the first letter capitalized
- std::string capFirstLetter(std::string text)
- {
- text[0] = toupper(text[0]);
- return text;
- }
- //Function prototype for str_replace
- std::string str_replace(std::string, std::string, std::string, bool);
- //Returns haystackl string with replacements instead of needles
- std::string str_replace(std::string haystack, std::string needle, std::string replacement, bool printErrors=false)
- {
- //Default result
- std::string result;
- //haystack must be >= needle
- if(haystack.size() >= needle.size())
- {
- int matchPos = -1;
- unsigned startPos=0;
- do
- {
- //Change startPos to end of last found needle and concatinate the string in between needles plus the replacement to the result string
- matchPos = haystack.find(needle, startPos);
- if(matchPos!=-1)
- {
- result += haystack.substr(startPos, (matchPos-startPos)) + replacement;
- startPos = matchPos+needle.size();
- }
- else
- result += haystack.substr(startPos, (haystack.size()-startPos));
- }
- while(matchPos!=-1);
- }
- else
- {
- if(printErrors)
- std::cout << "Error: Haystack string size is less than Needle string size" << std::endl;
- result = haystack;
- }
- return result;
- }
- //Function prototype for formatText
- std::string formatText(std::string, std::string);
- //Returns a colored string based on color choice
- std::string formatText(std::string text, std::string color)
- {
- /*
- reset 0 (everything back to normal)
- bold/bright 1 (often a brighter shade of the same colour)
- underline 4
- inverse 7 (swap foreground and background colours)
- bold/bright off 21
- underline off 24
- inverse off 27
- */
- //Convert color string to lowercase
- //No checking for spaces, if that is the other programmer's job
- for(unsigned i=0; i<color.size(); i++)
- color[i] = std::tolower(color[i]);
- //Choose formatCode - 0 default
- //background starts at 30, foreground starts at 40
- //add 60 to either for bright color variant
- int formatCode = 0;
- if(color == "bold")
- formatCode = 1;
- if(color == "underline")
- formatCode = 4;
- if(color == "inverse")
- formatCode = 7;
- if(color == "black")
- formatCode = 30;
- if(color == "red")
- formatCode = 31;
- if(color == "green")
- formatCode = 32;
- if(color == "yellow")
- formatCode = 33;
- if(color == "blue")
- formatCode = 34;
- if(color == "magenta")
- formatCode = 35;
- if(color == "cyan")
- formatCode = 36;
- if(color == "white")
- formatCode = 37;
- return "\033["+std::to_string(formatCode)+"m"+text+"\033[0m";
- }
- using namespace std;
- void setCursor(int, int);
- void setCursor(int row, int column)
- {
- cout << "\033["+to_string(row)+";"+to_string(column)+"H";
- }
- //Stores input and erases the contents that's converted into whitespace at specific (row,column) coords
- class TerminalEraser
- {
- private:
- string eraser;
- unsigned _row, _column;
- public:
- TerminalEraser(unsigned, unsigned);
- void resetPos(unsigned, unsigned);
- string capture(string);
- void erase();
- unsigned getSize();
- };
- TerminalEraser::TerminalEraser(unsigned row, unsigned column) : _row(row), _column(column){}
- //Set cursor position to another spot in the terminal
- void TerminalEraser::resetPos(unsigned row, unsigned column)
- {
- this->_row = row;
- this->_column = column;
- }
- //Capture input and repeat it back out
- string TerminalEraser::capture(string input)
- {
- string copy = input;
- //Strip away possible text format codes
- copy = str_replace(copy,"\033[0m","");
- copy = str_replace(copy,"\033[1m","");
- copy = str_replace(copy,"\033[4m","");
- copy = str_replace(copy,"\033[7m","");
- copy = str_replace(copy,"\033[30m","");
- copy = str_replace(copy,"\033[31m","");
- copy = str_replace(copy,"\033[32m","");
- copy = str_replace(copy,"\033[33m","");
- copy = str_replace(copy,"\033[34m","");
- copy = str_replace(copy,"\033[35m","");
- copy = str_replace(copy,"\033[36m","");
- copy = str_replace(copy,"\033[37m","");
- //Turn all chars into spaces
- for(unsigned i=0; i<copy.size(); i++)
- if(copy[i]!='\n' && copy[i]!='\t')
- copy[i] = ' ';
- this->eraser += copy;
- return input;
- }
- //Print the eraser
- void TerminalEraser::erase()
- {
- setCursor(this->_row,this->_column);
- cout << this->eraser;
- setCursor(this->_row,this->_column);
- this->eraser = "";
- }
- //Get size of buffer
- unsigned TerminalEraser::getSize()
- {
- return eraser.size();
- }
- enum Spot {Carrier, Battleship, Cruiser, Submarine, Destroyer, Water};
- struct Point
- {
- int row=0,column=0;
- Spot contents = Water;
- bool hit=false, miss=false;
- };
- class Ship
- {
- private:
- static unsigned lengths[5];
- static string names[5];
- static string colors[5];
- Spot type;
- unsigned HP=0;
- bool sunk=false;
- public:
- static unsigned getLength(Spot);
- static string getName(Spot);
- static string getColor(Spot);
- void setup(Spot, unsigned);
- Spot getType();
- bool isSunk();
- void operator--(int);
- };
- unsigned Ship::lengths[] = {5, 4, 3, 3, 2};
- string Ship::names[] = {"carrier","battleship","cruiser","submarine","destroyer"};
- string Ship::colors[] = {"red","green","yellow","blue","magenta"};
- unsigned Ship::getLength(Spot shipType)
- {
- return Ship::lengths[shipType];
- }
- string Ship::getName(Spot shipType)
- {
- return Ship::names[shipType];
- }
- string Ship::getColor(Spot shipType)
- {
- return Ship::colors[shipType];
- }
- //Fake constructor
- void Ship::setup(Spot shipType, unsigned hitPoints)
- {
- this->type = shipType;
- this->HP = hitPoints;
- }
- //Get the stored type of shipe
- Spot Ship::getType()
- {
- return this->type;
- }
- //Has the ship been sunk?
- bool Ship::isSunk()
- {
- return this->sunk;
- }
- //Change sunk to true if HP drops to 0
- void Ship::operator--(int n)
- {
- this->HP--;
- if(HP==0)
- this->sunk = true;
- }
- struct PlayerData
- {
- dyn_array<dyn_array<Point>> board_player;
- dyn_array<dyn_array<Point>> board_enemy;
- dyn_array<Ship> ships_player;
- dyn_array<Ship> ships_enemy;
- string playerName;
- string playerType;
- };
- string boardToString(dyn_array<dyn_array<Point>>);
- string boardToString(dyn_array<dyn_array<Point>> board)
- {
- string output="";
- output += " A B C D E F G H I J\n";
- for(unsigned row=0; row<10; row++)
- {
- if(row<9)
- output += " " + to_string(row+1);
- else
- output += to_string(row+1);
- for(unsigned column=0; column<10; column++)
- {
- if(board[row][column].contents != Water)
- {
- if(board[row][column].hit)
- output += formatText(" *", Ship::getColor(board[row][column].contents));
- else
- output += formatText(" @", Ship::getColor(board[row][column].contents));
- }
- else
- {
- if(board[row][column].miss)
- output += " #";
- else
- output += " ";
- }
- }
- output += "\n";
- }
- return output;
- }
- //TODO add recordMove()
- //Singleton class prototype for Game
- class Game
- {
- private:
- //Lock down constructor
- Game(){};
- static Game* _instance;
- unsigned numPlayers = 0;
- bool active=true;
- dyn_array<string> messages;
- dyn_array<dyn_array<Point>> cheat_board;
- void setupPlayerHuman(PlayerData&);
- void setupPlayerComputer(PlayerData&);
- Point makeMoveHuman(PlayerData&);
- Point makeMoveComputer(PlayerData&);
- public:
- static Game* getInstance();
- bool gameActive();
- void cheatBoard(PlayerData&);
- void setupPlayer(PlayerData&);
- void makeMove(PlayerData&,PlayerData&);
- void pushMessage(string);
- string pullMessage();
- };
- Game* Game::_instance = nullptr;
- Game* Game::getInstance()
- {
- //Ensure only 1 active instance
- if (_instance == nullptr)
- _instance = new Game();
- return _instance;
- }
- bool Game::gameActive()
- {
- return this->active;
- }
- void Game::cheatBoard(PlayerData& enemyPlayer)
- {
- this->cheat_board = enemyPlayer.board_player;
- }
- void Game::setupPlayerHuman(PlayerData& player)
- {
- //Controllers that handle printing, and erasing of the screen
- TerminalEraser instructionPrinter(1,1), inputPrinter(1,1), boardPrinter(6,1), shipListPrinter(22,1), errorPrinter(18,1), shipTitle(21,1), commandTitle(19,1);
- string playerInput;
- dyn_array<Ship> boardList, shipList;
- shipList = player.ships_player;
- //Get player's name
- cout << inputPrinter.capture("Alright Player"+to_string(this->numPlayers)+", let's get your name and setup the board!\nInput: ");
- getline(cin, playerInput);
- inputPrinter.capture(playerInput+"\n");
- player.playerName = playerInput;
- //Make sure player is ready
- cout << inputPrinter.capture("Howdy " + player.playerName + "! Let's get started!\n");
- cout << inputPrinter.capture(" (make sure no one is looking and enter \"ready\" to continue.)\n");
- do
- {
- cout << inputPrinter.capture(" status: ");
- getline(cin, playerInput);
- inputPrinter.capture(playerInput+"\n");
- playerInput = removeWhiteSpace(playerInput);
- playerInput = allLowerCase(playerInput);
- if(playerInput!="ready")
- cout << inputPrinter.capture("\nOk, come on... all you need to do is type \"ready\". Try again...\n");
- }
- while(playerInput!="ready");
- //Erase everything above
- inputPrinter.erase();
- //Print out instructions
- cout << instructionPrinter.capture("Place EACH ship on the board. (ships can be replaced multiple times)\n");
- cout << instructionPrinter.capture("Enter \"confirm\" once you're ready to move on.\n");
- cout << instructionPrinter.capture("Format: place getName x(A-J),y(1-10),rotation(0-1)\n");
- cout << instructionPrinter.capture("Ex: place Battleship A,3,1\n");
- //Print out the titles
- setCursor(19,1);
- cout << commandTitle.capture("Command: ");
- setCursor(21,1);
- cout << shipTitle.capture("Ships:");
- setCursor(18,1);
- cout << errorPrinter.capture("Enter a command...");
- //Reset the inputPrinter to just after "Command: "
- inputPrinter.resetPos(19,10);
- //Main loop for placing ship logic
- while(!(shipList.getSize() == 0 && playerInput=="confirm"))
- {
- //Print current board layout
- boardPrinter.erase();
- cout << boardPrinter.capture(boardToString(player.board_player)+"\n\n");
- //Print ships that have yet to be placed
- shipListPrinter.erase();
- for(unsigned i=0; i<shipList.getSize(); i++)
- cout << shipListPrinter.capture(formatText(capFirstLetter(Ship::getName(shipList[i].getType())), Ship::getColor(shipList[i].getType()))+"\n");
- if(shipList.getSize() == 0)
- shipTitle.erase();
- //Accept player commands (erase last command)
- setCursor(19,10);
- getline(cin, playerInput);
- inputPrinter.capture(playerInput+"\n");
- inputPrinter.erase();
- //Main loop for trying to place ships + input validation
- playerInput = allLowerCase(playerInput);
- if(playerInput!="confirm")
- {
- dyn_array<string> args = str_explode(playerInput, " ");
- if(args.getSize() == 3)
- {
- if(args[0] == "place")
- {
- int shipListMatch=-1, boardListMatch=-1;
- Ship currentShip;
- //Search shipList for matching getName
- for(unsigned i=0; i<shipList.getSize(); i++)
- {
- if(args[1] == Ship::getName(shipList[i].getType()))
- {
- shipListMatch = i;
- currentShip = shipList[i];
- }
- }
- //Search boardList for matching getName
- for(unsigned i=0; i<boardList.getSize(); i++)
- {
- if(args[1] == Ship::getName(boardList[i].getType()))
- {
- boardListMatch = i;
- currentShip = boardList[i];
- }
- }
- if(shipListMatch>=0 || boardListMatch>=0)
- {
- int inputColumn=-1, InputRow=-1, inputRotation=-1;
- //Gather coordinates and rotation
- dyn_array<string> coords = str_explode(args[2], ",");
- if(coords.getSize() == 3)
- {
- string arg1 = coords[0];
- string arg2 = coords[1];
- string arg3 = coords[2];
- //Input validation for coordintes
- if(arg1.size() == 1 && static_cast<int>(arg1[0])>=97 && static_cast<int>(arg1[0])<=106)
- {
- inputColumn = static_cast<int>(arg1[0])-97;
- if(isInt(arg2, true))
- {
- int num = stoi(arg2);
- if(num>= 1 && num <=10)
- {
- InputRow = num-1;
- if(arg3.size() == 1 && (arg3[0] == '1' || arg3[0] == '0'))
- {
- inputRotation = stoi(arg3);
- unsigned shipLength = Ship::getLength(currentShip.getType());
- bool placement = true, shipFit=true;
- if(inputRotation==0)
- shipFit = (shipLength+inputColumn)<=10;
- else
- shipFit = (shipLength+InputRow)<=10;
- if(shipFit)
- {
- for(unsigned i=0; i<shipLength; i++)
- {
- if(inputRotation==0)
- {
- if(player.board_player[InputRow][inputColumn+i].contents != Water)
- placement = false;
- }
- else
- {
- if(player.board_player[InputRow+i][inputColumn].contents != Water)
- placement = false;
- }
- }
- //Place the ship on the board once a valid spot is found
- if(placement)
- {
- if(shipListMatch>=0)
- {
- boardList.push(currentShip);
- shipList.deleteItem(shipListMatch);
- }
- //Erase the ship from the board if it exists already
- if(boardListMatch>=0)
- {
- for(unsigned row=0; row<10; row++)
- {
- for(unsigned column=0; column<10; column++)
- {
- if(player.board_player[row][column].contents == boardList[boardListMatch].getType())
- player.board_player[row][column].contents = Water;
- }
- }
- }
- //Place the ship on the board spot by spot
- for(unsigned i=0; i<shipLength; i++)
- {
- if(inputRotation==0)
- player.board_player[InputRow][inputColumn+i].contents = currentShip.getType();
- else
- player.board_player[InputRow+i][inputColumn].contents = currentShip.getType();
- }
- errorPrinter.erase();
- 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)+")");
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("There seem's to be another ship in the way of your placement.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("You ship won't fit on the board using the coordinates: ("+to_string(inputColumn)+","+to_string(InputRow+1)+")");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Ship coordinate \"rotation\" must be either 0 or 1.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Ship coordinate \"row\" must be 1-10.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Ship coordinate \"row\" must be a number.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Ship coordinate \"column\" must be A-J.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Ship coordinate scheme requires 3 comma seperated arguments.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("That ship doesn't exist.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Invalid first argument. 1st arg must either be \"place\" or \"confirm\".");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Invalid command.");
- }
- }
- else
- {
- if(shipList.getSize()>0)
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("You still have ships left to place!");
- }
- }
- }
- //Clear the screen of clutter
- instructionPrinter.erase();
- commandTitle.erase();
- shipTitle.erase();
- boardPrinter.erase();
- errorPrinter.erase();
- shipListPrinter.erase();
- setCursor(1,1);
- }
- void Game::setupPlayerComputer(PlayerData& player)
- {
- //Set the computer's name
- player.playerName = "Computer"+to_string(this->numPlayers);
- for(int i=0; i<5; i++)
- {
- bool placement=false;
- Spot currentShip = static_cast<Spot>(i);
- unsigned shipLength = Ship::getLength(currentShip);
- int tryRow, tryColumn, rotation;
- //Loop until valid ship spot is found (pick point, rotate vert/horiz, pick new point, repeat)
- while(!placement)
- {
- //make placement true unless proven false
- placement = true;
- //randomly choose rotation and coords (max board size - length of ship)
- tryRow = getRandom(0, (10-shipLength));
- tryColumn = getRandom(0, (10-shipLength));
- for(unsigned j=0; j<shipLength; j++)
- {
- if(rotation==0)
- {
- if(player.board_player[tryRow][tryColumn+j].contents != Water)
- placement = false;
- }
- else
- {
- if(player.board_player[tryRow+j][tryColumn].contents != Water)
- placement = false;
- }
- }
- if(!placement)
- {
- //reset placement and invert rotation
- placement = true;
- if(rotation==0)
- rotation = 1;
- else
- rotation = 0;
- for(unsigned j=0; j<shipLength; j++)
- {
- if(rotation==0)
- {
- if(player.board_player[tryRow][tryColumn+j].contents != Water)
- placement = false;
- }
- else
- {
- if(player.board_player[tryRow+j][tryColumn].contents != Water)
- placement = false;
- }
- }
- }
- }
- //Place the ship on the board once a valid spot is found
- for(unsigned j=0; j<shipLength; j++)
- {
- if(rotation==0)
- player.board_player[tryRow][tryColumn+j].contents = currentShip;
- else
- player.board_player[tryRow+j][tryColumn].contents = currentShip;
- }
- }
- }
- void Game::setupPlayer(PlayerData& player)
- {
- this->numPlayers++;
- //Fill both boards with a 10x10 grid of Point objects
- for(unsigned row=0; row<10; row++)
- {
- player.board_player.push(dyn_array<Point>());
- player.board_enemy.push(dyn_array<Point>());
- for(unsigned column=0; column<10; column++)
- {
- player.board_player[row].push(Point());
- player.board_player[row][column].row=row;
- player.board_player[row][column].column=column;
- player.board_enemy[row].push(Point());
- player.board_enemy[row][column].row=row;
- player.board_enemy[row][column].column=column;
- }
- }
- //Instantiate and setup ship objects (type, hitpoints)
- for(unsigned i=0; i<5; i++)
- player.ships_player.push(Ship());
- player.ships_player[Carrier].setup(Carrier, 5);
- player.ships_player[Battleship].setup(Battleship, 4);
- player.ships_player[Cruiser].setup(Cruiser, 3);
- player.ships_player[Submarine].setup(Submarine, 3);
- player.ships_player[Destroyer].setup(Destroyer, 2);
- player.ships_enemy = player.ships_player;
- if(player.playerType == "human")
- {
- this->setupPlayerHuman(player);
- }
- if(player.playerType == "computer")
- this->setupPlayerComputer(player);
- }
- Point Game::makeMoveHuman(PlayerData& p1)
- {
- //TODO add documentation
- //TODO allow quit
- TerminalEraser inputPrinter(1,1), instructionPrinter(27,1), boardPlayerTitle(1,1), boardPlayerPrinter(2,1), boardEnemyTitle(14,1), boardEnemyPrinter(15,1), errorPrinter(31,1);
- string playerInput;
- //Print out stored messages
- string message = this->pullMessage();
- if(message!="")
- {
- while(message!="")
- {
- cout << inputPrinter.capture(message+"\n");
- message = this->pullMessage();
- }
- cout << inputPrinter.capture("\n");
- }
- //Make sure player is ready
- 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");
- do
- {
- cout << inputPrinter.capture(" status: ");
- getline(cin, playerInput);
- inputPrinter.capture(playerInput+"\n");
- playerInput = removeWhiteSpace(playerInput);
- playerInput = allLowerCase(playerInput);
- if(playerInput!="ready")
- cout << inputPrinter.capture("\nOk, come on... all you need to do is type \"ready\". Try again.\n");
- }
- while(playerInput!="ready");
- //Erase everything above
- inputPrinter.erase();
- instructionPrinter.erase();
- cout << instructionPrinter.capture("Look at the enemy's board, and select a point to shoot.("+formatText(formatText("+", "cyan"), "bold")+")\n");
- cout << instructionPrinter.capture("Format: letter,number (Ex: A,3)\n");
- cout << instructionPrinter.capture("You can replace coordinates, or enter \"confirm\" to continue.\n");
- boardPlayerTitle.erase();
- cout << boardPlayerTitle.capture(formatText(formatText("====== Your Board =======\n", "green"), "bold"));
- boardEnemyTitle.erase();
- cout << boardEnemyTitle.capture(formatText(formatText("===== Enemy's Board =====\n", "red"), "bold"));
- errorPrinter.erase();
- cout << errorPrinter.capture("Eneter a coordinate...");
- inputPrinter.resetPos(32,1);
- int shotRow=-1, shotColumn=-1, testRow, testColumn;
- dyn_array<string> args;
- while(playerInput!="confirm")
- {
- boardPlayerPrinter.erase();
- cout << boardPlayerPrinter.capture(boardToString(p1.board_player));
- boardEnemyPrinter.erase();
- if(playerInput=="cheat")
- {
- cout << boardEnemyPrinter.capture(boardToString(this->cheat_board));
- errorPrinter.erase();
- cout << errorPrinter.capture("You cheated!!!");
- }
- else
- cout << boardEnemyPrinter.capture(boardToString(p1.board_enemy));
- if(shotRow!=-1 && shotColumn!=-1)
- {
- setCursor((shotRow+1)+15,((shotColumn+1)*2)+2);
- cout << formatText(formatText("+", "cyan"), "bold");
- }
- inputPrinter.erase();
- cout << inputPrinter.capture("Command: ");
- getline(cin, playerInput);
- inputPrinter.capture(playerInput+"\n");
- playerInput = removeWhiteSpace(playerInput);
- playerInput = allLowerCase(playerInput);
- testColumn=-1;
- testRow=-1;
- if(playerInput!="confirm")
- {
- if(playerInput!="cheat")
- {
- args = str_explode(playerInput, ",");
- if(args.getSize() == 2)
- {
- if(args[0].size() == 1 && static_cast<int>(args[0][0])>=97 && static_cast<int>(args[0][0])<=106)
- {
- testColumn = static_cast<int>(args[0][0])-97;
- if(isInt(args[1], true))
- {
- testRow = stoi(args[1]);
- if(testRow>=1 && testRow<=10)
- {
- if(!p1.board_enemy[testRow-1][testColumn].hit && !p1.board_enemy[testRow-1][testColumn].miss)
- {
- shotColumn = testColumn;
- shotRow = testRow-1;
- errorPrinter.erase();
- cout << errorPrinter.capture("Nice shot! You chose: "+string(1,char(shotColumn+65))+","+to_string(shotRow+1));
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("That spot has already been fired upon.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("The second coordinate must be 1-10.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("The second coordinate must be a non-zero integer.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("The first coordinate must be A-J.");
- }
- }
- else
- {
- errorPrinter.erase();
- cout << errorPrinter.capture("Invalid command. Either comma seperated coordinates or \"confirm\".");
- }
- }
- }
- else
- {
- if(shotRow==-1 && shotColumn==-1)
- {
- playerInput="";
- errorPrinter.erase();
- cout << errorPrinter.capture("You haven't selected a valid point yet!");
- }
- }
- }
- //Clear the terminal
- inputPrinter.erase();
- instructionPrinter.erase();
- boardEnemyTitle.erase();
- boardEnemyPrinter.erase();
- boardPlayerTitle.erase();
- boardPlayerPrinter.erase();
- errorPrinter.erase();
- setCursor(1,1);
- //Return selected point by value
- return p1.board_enemy[shotRow][shotColumn];
- }
- Point Game::makeMoveComputer(PlayerData& p1)
- {
- //make this the LAST thing you work on!!!
- //TODO make sure it hasn't been fired on before
- //if you find a ship, keep track of what ship it is, and what size it is
- //then keep firing on that ship until it is destroyed
- //then search for aother random point
- Point pt;
- pt.row = getRandom(0,2);
- pt.column = getRandom(0,2);
- return pt;
- }
- void Game::makeMove(PlayerData& p1, PlayerData& p2)
- {
- Point shotAttempt, shotResult;
- if(p1.playerType == "human")
- shotAttempt = this->makeMoveHuman(p1);
- if(p1.playerType == "computer")
- shotAttempt = this->makeMoveComputer(p1);
- //Grab targeted point
- shotResult = p2.board_player[shotAttempt.row][shotAttempt.column];
- if(shotResult.contents != Water)
- {
- shotResult.hit = true;
- p2.ships_player[shotResult.contents]--;
- p1.ships_enemy[shotResult.contents]--;
- }
- else
- {
- shotResult.miss = true;
- }
- p1.board_enemy[shotResult.row][shotResult.column] = shotResult;
- p2.board_player[shotResult.row][shotResult.column] = shotResult;
- if(p1.playerType=="human")
- {
- if(shotResult.hit)
- {
- this->pushMessage("You hit " + p2.playerName + "'s " + capFirstLetter(Ship::getName(shotResult.contents)) +"!");
- if(p2.ships_player[shotResult.contents].isSunk())
- this->pushMessage("You just sunk " + p2.playerName + "'s " + capFirstLetter(Ship::getName(shotResult.contents)) + "!!!");
- }
- else
- this->pushMessage("You missed!");
- //Print out stored messages
- TerminalEraser screenPrint(1,1);
- string output = this->pullMessage();
- while(output!="")
- {
- cout << screenPrint.capture(output+"\n");
- output = this->pullMessage();
- }
- //Wait for player to enter "continue"
- cout << screenPrint.capture("\nPlease enter \"continue\" to proceeed...\n");
- do
- {
- cout << screenPrint.capture(" Input: ");
- getline(cin, output);
- screenPrint.capture(output+"\n");
- output = removeWhiteSpace(output);
- output = allLowerCase(output);
- if(output!="continue")
- cout << screenPrint.capture("\n Ok, come on... all you need to do is type \"continue\". Try again.\n");
- }
- while(output!="continue");
- //Erase everything above
- screenPrint.erase();
- }
- }
- void Game::pushMessage(string text)
- {
- this->messages.push(text);
- }
- string Game::pullMessage()
- {
- string text="";
- if(this->messages.getSize() > 0)
- {
- text = messages[0];
- messages.deleteItem(0);
- }
- return text;
- }
- /*
- bool Game::allSunk()
- {
- if(this->ships[Carrier].sunk && this->ships[Battleship].sunk && this->ships[Cruiser].sunk && this->ships[Submarine].sunk && this->ships[Destroyer].sunk)
- return true;
- else
- return false;
- }
- */
- int main()
- {
- system("clear");
- TerminalEraser mainOutput(1,1);
- cout << mainOutput.capture("Welcome to the game of Battleship!\n");
- dyn_array<PlayerData> players;
- string input;
- for(unsigned i=0; i<2; i++)
- {
- cout << mainOutput.capture("\nWill Player" + to_string(i+1) + " be human? (yes/no)\nChoice: ");
- do
- {
- getline(cin, input);
- mainOutput.capture(input+"\n");
- input = removeWhiteSpace(input);
- for(unsigned i=0; i<input.size(); i++)
- input[i] = tolower(input[i]);
- if(input != "yes" && input!="no")
- cout << mainOutput.capture("\nThis is really simple... either 'yes' or 'no'. Try again!\nChoice: ");
- else
- {
- if(input=="yes")
- {
- players.push(PlayerData());
- players[i].playerType = "human";
- }
- else
- {
- players.push(PlayerData());
- players[i].playerType = "computer";
- }
- }
- }
- while(input != "yes" && input!="no");
- }
- mainOutput.erase();
- //get players ready
- for(unsigned i=0; i<2; i++)
- Game::getInstance()->setupPlayer(players[i]);
- PlayerData& currentPlayer = players[0];
- PlayerData& otherPlayer = players[1];
- bool keepPlaying = true;
- //currentPlayer->allSunk()
- while(keepPlaying)
- {
- //grab the other player's board
- Game::getInstance()->cheatBoard(otherPlayer);
- if(currentPlayer.playerType == "human")
- {
- Game::getInstance()->pushMessage("You have 5 moves left!");
- Game::getInstance()->pushMessage("Also, I'm super hungry and tired. Can you help?");
- }
- Game::getInstance()->makeMove(currentPlayer, otherPlayer);
- mainOutput.erase();
- swapper(currentPlayer, otherPlayer);
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement