Tic Tac Toe in Java - Computer wins but the message is not displayed and the program keeps running - list

I have wrote a tic tac toe program. However, when the computer won, the message is not displayed and the program keeps running. But after copy the sample answer, it works.
The problem is: I couldn't figure out what's the problem with my code since it was not much difference with the sample answer.
Is it the problem of my static char [][] board?
My code:
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;
public class TicTacToe {
static char[][] board = {{' ', '|', ' ', '|', ' '},
{'-', '+', '-', '+', '-'},
{' ', '|', ' ', '|', ' '},
{'-', '+', '-', '+', '-'},
{' ', '|', ' ', '|', ' '}};
static ArrayList<Integer> plyPositions = new ArrayList<Integer>();
static ArrayList<Integer> cpuPositions = new ArrayList<Integer>();
public static void main(String[] args) {
printBoard();
while (true) {
//player
Scanner sc = new Scanner(System.in);
System.out.print("Enter your placement [1-9]: ");
int ppos = sc.nextInt();
while (plyPositions.contains(ppos) || cpuPositions.contains(ppos)) {
System.out.println("Positions taken! Enter a correct position.");
ppos = sc.nextInt();
}
move(ppos, "player");
plyPositions.add(ppos);
String result = checkWin();
if (result.length() > 0) {
System.out.println(result);
break;
}
//computer
Random r = new Random();
int cpos = r.nextInt(9) + 1;
while (plyPositions.contains(cpos) || cpuPositions.contains(cpos)) {
cpos = r.nextInt(9) + 1;
}
System.out.println("Computer's placement: " + cpos);
move(cpos, "cpu");
cpuPositions.add(cpos);
if (result.length() > 0) {
System.out.println(result);
break;
}
}
}
public static String checkWin() {
List topRow = Arrays.asList(1, 2, 3);
List midRow = Arrays.asList(4, 5, 6);
List botRow = Arrays.asList(7, 8, 9);
List leftCol = Arrays.asList(1, 4, 7);
List midCol = Arrays.asList(2, 5, 8);
List rightCol = Arrays.asList(3, 6, 9);
List cross1 = Arrays.asList(1, 5, 9);
List cross2 = Arrays.asList(3, 5, 7);
List<List> winning = new ArrayList<>();
winning.add(topRow);
winning.add(midRow);
winning.add(botRow);
winning.add(leftCol);
winning.add(midCol);
winning.add(rightCol);
winning.add(cross1);
winning.add(cross2);
for (List l : winning) {
if (plyPositions.containsAll(l)) {
return "Congratulations! You win :D";
}
}
for (List l : winning) {
if (cpuPositions.containsAll(l)) {
return "Oops... computer won :(";
}
}
if (plyPositions.size() + cpuPositions.size() == 9) {
return "Fair";
}
return "";
}
public static void move(int pos, String user) {
char symbol = ' ';
if (user.equals("player")) {
symbol = 'O';
} else if (user.equals("cpu")) {
symbol = 'X';
}
switch (pos) {
case 1:
board[0][0] = symbol;
printBoard();
break;
case 2:
board[0][2] = symbol;
printBoard();
break;
case 3:
board[0][4] = symbol;
printBoard();
break;
case 4:
board[2][0] = symbol;
printBoard();
break;
case 5:
board[2][2] = symbol;
printBoard();
break;
case 6:
board[2][4] = symbol;
printBoard();
break;
case 7:
board[4][0] = symbol;
printBoard();
break;
case 8:
board[4][2] = symbol;
printBoard();
break;
case 9:
board[4][4] = symbol;
printBoard();
break;
default:
break;
}
}
public static void printBoard() {
for (char[] row : board) {
for (char c : row) {
System.out.print(c);
}
System.out.println();
}
}
}
Sample code (it works well):
import java.util.*;
public class TicTacToe {
static ArrayList<Integer> playerPositions = new ArrayList<Integer>();
static ArrayList<Integer> cpuPositions = new ArrayList<Integer>();
public static void main(String[] args) {
char[][] gameBoard = {
{' ', '|', ' ', '|', ' '},
{'-', '+', '-', '+', '-'},
{' ', '|', ' ', '|', ' '},
{'-', '+', '-', '+', '-'},
{' ', '|', ' ', '|', ' '}
};
printGameBoard(gameBoard);
while (true) {
Scanner scan = new Scanner(System.in);
System.out.println("Enter your placement (1-9):");
int playerPos = scan.nextInt();
while (playerPositions.contains(playerPos) || cpuPositions.contains(playerPos)) {
System.out.println("position taken! Enter a correct Position");
playerPos = scan.nextInt();
}
placePiece(gameBoard, playerPos, "player");
printGameBoard(gameBoard);
String result = checkWinner();
if (result.length() > 0) {
System.out.println(result);
break;
}
Random rand = new Random();
int cpuPos = rand.nextInt(9) + 1;
while (playerPositions.contains(cpuPos) || cpuPositions.contains(cpuPos)) {
cpuPos = rand.nextInt(9) + 1;
}
placePiece(gameBoard, cpuPos, "cpu");
printGameBoard(gameBoard);
result = checkWinner();
if (result.length() > 0) {
System.out.println(result);
break;
}
}
}
public static void printGameBoard(char[][] gameBoard) {
for (char[] row : gameBoard) {
for (char c : row) {
System.out.print(c);
}
System.out.println();
}
}
public static void placePiece(char[][] gameBoard, int pos, String user) {
char symbol = ' ';
if (user.equals("player")) {
symbol = 'X';
playerPositions.add(pos);
} else if (user.equals("cpu")) {
symbol = 'O';
cpuPositions.add(pos);
}
switch (pos) {
case 1:
gameBoard[0][0] = symbol;
break;
case 2:
gameBoard[0][2] = symbol;
break;
case 3:
gameBoard[0][4] = symbol;
break;
case 4:
gameBoard[2][0] = symbol;
break;
case 5:
gameBoard[2][2] = symbol;
break;
case 6:
gameBoard[2][4] = symbol;
break;
case 7:
gameBoard[4][0] = symbol;
break;
case 8:
gameBoard[4][2] = symbol;
break;
case 9:
gameBoard[4][4] = symbol;
break;
default:
break;
}
}
public static String checkWinner() {
List topRow = Arrays.asList(1, 2, 3);
List midRow = Arrays.asList(4, 5, 6);
List botRow = Arrays.asList(7, 8, 9);
List leftCol = Arrays.asList(1, 2, 7);
List midCol = Arrays.asList(2, 5, 8);
List rightCol = Arrays.asList(3, 6, 9);
List cross1 = Arrays.asList(1, 5, 9);
List cross2 = Arrays.asList(3, 5, 7);
List<List> winConditions = new ArrayList<List>();
winConditions.add(topRow);
winConditions.add(midRow);
winConditions.add(botRow);
winConditions.add(leftCol);
winConditions.add(midCol);
winConditions.add(rightCol);
winConditions.add(cross1);
winConditions.add(cross2);
for (List l : winConditions) {
if (playerPositions.containsAll(l)) {
return "Congratulations you won!";
}
}
for (List l : winConditions) {
if (cpuPositions.containsAll(l)) {
return "CPU wins! Sorry :-(";
}
}
if (playerPositions.size() + cpuPositions.size() == 9) {
return "CAT";
}
return "";
}
}

Related

C++ User inputs a char, the variable stays 0

I'm trying to create a tic-tac-toe game where the user inputs a number (corresponding to the position he wants place his X or O), but the variable (Move) which receives the number stays at 0 no matter what the input is. Can you help me figure out what to fix so the variable actually receives what the user inputs? Here is the code for the function which receives the move:
int FDeclarations::GetMove(){
int Move;
std::cin >> Move;
return Move;}
Here is the code for the function which goes through the switch statement (none of the works since the variable "Move" is always at 0)
int FDeclarations::PlaceMove(){
switch (Move)
{
case(1):
if (turn == false) { TopLeft = 'x'; }
else { TopLeft = 'o'; }
break;
case(2):
if (turn = false) { TopMid = 'x'; }
else { TopMid = 'o'; }
break;
case(3):
if (turn = false) { TopRight = 'x'; }
else { TopRight = 'o'; }
break;
case(4):
if (turn = false) { MidLeft = 'x'; }
else { MidLeft = 'o'; }
break;
case(5):
if (turn = false) { MidMid = 'x'; }
else { MidMid = 'o'; }
break;
case(6):
if (turn = false) { MidRight = 'x'; }
else { MidRight = 'o'; }
break;
case(7):
if (turn = false) { BotLeft = 'x'; }
else { BotLeft = 'o'; }
break;
case(8):
if (turn = false) { BotMid = 'x'; }
else { BotMid = 'o'; }
break;
case(9):
if (turn = false) { BotRight = 'x'; }
else { BotRight = 'o'; }
break;
}
table();
return 0;
}
Here are my variable declarations:
class FDeclarations
{
public:
int PlaceMove();
int GetMove();
int CheckWin();
void table();
private:
bool turn = false;
int Move;
char TopLeft = '1';
char TopMid = '2';
char TopRight = '3';
char MidLeft = '4';
char MidMid = '5';
char MidRight = '6';
char BotLeft = '7';
char BotMid = '8';
char BotRight ='9';
bool XWin;
bool OWin;
};
In your function
int FDeclarations::GetMove() {
int Move;
std::cin >> Move;
return Move;
}
You declare a new variable called Move which is local to that function. This is different from the member variable Move declared in the class. C++ will prefer to bind to the function-level variable.
Unless you use the return value of GetMove to set the member variable Move in code you've not shown us, then the member variable Move will never change, causing your problem.
In FDeclarations::GetMove() you need to set the private member of your class Move to whatever the user will input instead of the local variable wich shadows the member one. So a quick fix will be:
int FDeclarations::GetMove(){
std::cin >> Move;
return Move;}

Switch statement will NOT print a string

char gamerCentral::getGamerTag( )
{
switch(gamerTag)
{
case '1': return gamerTag = "Diamond";
break;
case '2': return gamerTag = "Silver";
break;
case '3': return gamerTag = "Bronze";
break;
case '4': return gamerTag = "Wood";
break;
default: return gamerTag = "Uninstall";
break;
}
char gamerTag;
GamerClub::GamerClub(
char tag)
{
gamerTag = tag;
}
I'm trying to return the gamerTag, but it says that it cannot convert to a string. Is their a way to convert the char to a string inside the switch statement?
Have your function return a string, it can still operate on the single character as input. Here's an example.
std::string getGamerTag(char t)
{
switch(t)
{
case '1': return "Diamond";
case '2': return "Silver";
// ...
}
return "";
}
// prints "Silver"
cout << getGamerTag('2');
use string variable or use char*
char* getGamerTag(int t)
{
switch(t)
{
case 1: return "Diamond";
case 2: return "Silver";
}
}
int main()
{
cout << getGamerTag(2);
}
I recommend using an array of strings:
std::string getGamerTag(unsigned int t)
{
static const char * tag_names[] =
{
"Diamond", "Silver", "Bronze", "Wood", "Unistall",
};
static const unsigned int name_quantity =
sizeof(tag_names) / sizeof(tag_names[0]);
std::string name;
if ((t > 0) && (t <= name_quantity))
{
name = tag_names[t - 1];
}
return name;
}

Parsing a string : my code apparentely works after testing, but is inelegant

This is a c++ question. I want the code of the desired c++ function (see below) to be (as much as possible) in c-style and using c string library functions because I think that this will lead the quickest code in terms of execution time. (Am I wrong ? If so, how much ?) Yes, I value performance more than readability for this question because the desired function will be called a lot (millions) of times.
I am receiving const char *'s of the form "25Dec2016" that represent dates and I am parsing them to back out from them three int's (one for the day, the second for the month and the last for the year (that I assumed to be a number between 0 and 9999)) thanks to a function
Parse(const char * cDate, int & day, int & month, int & year)
I coded such a function and tested it : it works on correct const char*s (those that indeed represent date in my format), but I feel that my use of c functions (atoi for instance) is incorrect, even if I don't see why. There are also other problems :
the code is inelegant (the big if ... else if ... if) : one cannot do a switch statement on a string, but is there an elegant way to do this without resorting the std::map and c++11 ?
surely problematic from a c string point of view (I am not an expert) : for instance, I am really not happy with the way I extract the three substring into "buffers" ... Plus it appears I have problems with not null terminated char arrays that I'd like to correct. I could force a \0 at the end of _day and _year as I did for _month but I find that doing so is awful, so that I suspect a bad "design" of my parsing function
quite bad from an error handling point of view : the function is not a constructor for now, but could finally be, this is the reason why I throw.
I am open to any comments !
Here is the initial code :
Parse(const char * cDate, int & day, int & month, int & year)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format";
}
else
{
char _day[2];
char _month[4];
char _year[5]; // implictely the biggest year we authorize is 99999 ; it should suffice
for (int i = 0; i < 2; ++i)
{
_day[i] = cDate[i];
}
day = atoi(_day); // if fail, Undefined behaviour, see strtol for a more robust cross-platform alternative
char c;
for (int i = 2; i < 5; ++i)
{
c = cDate[i];
_month[i-2] = toupper(c);
}
_month[3] = '\0';
if (0 == strcmp("JAN", _month))
{
month = 1;
}
else if (0 == strcmp("FEB", _month))
{
month = 2;
}
else if (0 == strcmp("MAR", _month))
{
month = 3;
}
else if (0 == strcmp("APR",_month))
{
month = 4;
}
else if (0 == strcmp("MAY", _month))
{
month = 5;
}
else if (0 == strcmp("JUN", _month))
{
month = 6;
}
else if (0 == strcmp("JUL", _month))
{
month = 7;
}
else if (0 == strcmp("AUG", _month))
{
month = 8;
}
else if (0 == strcmp("SEP", _month))
{
month = 9;
}
else if (0 == strcmp("OCT",_month))
{
month = 10;
}
else if (0 == strcmp("NOV", _month))
{
month = 11;
}
else if (0 == strcmp("DEC", _month))
{
month = 12;
}
else
{
throw "Error : invalid month string";
}
for (int i = 5; i < 10; ++i)
{
_year[i-5] = cDate[i];
}
year = atoi(_year);
}
}
}
I finally opted for the function to be a constructor of a Date class, and inspired myself from rici's answer also using strtol as I intended initially (see comment in my initial code) instead of atoi:
#include <cstring> // for strlen
#include <ctype.h> // for toppuer
#include <stdlib.h>
int up(char c)
{
return toupper((unsigned char)(c));
}
Date::Date(const char * cDate)
{
if (0 == cDate)
{
throw "Error : null string pointer";
}
else
{
if (strlen(cDate) < 8)
{
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case"; // for now, valid format is 24Oct1979
}
else
{
char * ppEnd;
int day = strtol(cDate, &ppEnd, 10);
if (0 == day)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Day = day;
char cMonth[4];
int month;
memcpy(cMonth, &ppEnd[0], 3);
switch (up(cMonth[0]))
{
case 'A':
{
switch (up(cMonth[1]))
{
case 'P': if (up(cMonth[2]) == 'R') month = 4;
break;
case 'U': if (up(cMonth[2]) == 'G') month = 8;
break;
}
break;
}
case 'D':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'C')
month = 12;
break;
}
case 'F':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'B')
month = 2;
break;
}
case 'J':
{
switch (up(cMonth[1]))
{
case 'A': if (up(cMonth[2]) == 'N')
month = 1;
break;
case 'U': switch (up(cMonth[2]))
{
case 'N': month = 6;
case 'L': month = 7;
}
break;
}
break;
}
case 'M':
{
if (up(cMonth[1]) == 'A')
{
switch (up(cMonth[2]))
{
case 'R': month = 3;
case 'Y': month = 5;
}
}
break;
}
case 'N':
{
if (up(cMonth[1]) == 'O' && up(cMonth[2]) == 'V') month = 11;
break;
}
case 'O':
{
if (up(cMonth[1]) == 'C' && up(cMonth[2]) == 'T') month = 10;
break;
}
case 'S':
{
if (up(cMonth[1]) == 'E' && up(cMonth[2]) == 'P') month = 9;
break;
}
}
m_Month = (Month)month;
int year = strtol(ppEnd + 3, &ppEnd, 10);
if (0 == year)
throw "Error : invalid string format. String format is DDMMMYYYY with M's in upper or lower case";
m_Year = year;
updateSerial();
}
}
}
Remark. Being lazy, I did not throw everywhere I should in the "month" part of the code.
If your system is Posix-compatible, then you could just use strptime with the format %d%b%Y:
bool Parse(const char* date, int& day, int& month, int& year) {
struct tm components;
const char* rest = strptime(date, "%d%b%Y", &components);
if (rest == NULL || *rest != '\0') return false;
day = components.tm_mday;
month = components.tm_mon;
year = components.tm_year + 1900;
return true;
}
That is likely to be as fast as a naive parser, and it is certainly a lot easier to write :)
Otherwise, you should use strtol rather than atoi, since it will let you know both whether the parse was successful and where the next character is. And if you want to parse the month names quickly, you'll want to build a trie, either as a table or directly in code (the table is probably faster, fwiw):
static int up(char c) { return toupper((unsigned char)(c)); }
int parseMonth(const char* p) {
switch (up(p[0])) {
case 'A': {
switch (up(p[1])) {
case 'P': if (up(p[2]) == 'R') return 4;
break;
case 'U': if (up(p[2]) == 'G') return 8;
break;
}
break;
}
case 'D': {
if (up(p[1]) == 'E' && up(p[2]) == 'C') return 12;
break;
}
case 'F': {
if (up(p[1]) == 'E' && up(p[2]) == 'B') return 2;
break;
}
case 'J': {
switch (up(p[1])) {
case 'A': if (up(p[2]) == 'N') return 1;
break;
case 'U': switch (up(p[2])) {
case 'N': return 6;
case 'L': return 7;
}
break;
}
break;
}
case 'M': {
if (up(p[1]) == 'A') {
switch (up(p[2])) {
case 'R': return 3;
case 'Y': return 5;
}
}
break;
}
case 'N': {
if (up(p[1]) == 'O' && up(p[2]) == 'V') return 11;
break;
}
case 'O': {
if (up(p[1]) == 'C' && up(p[2]) == 'T') return 10;
break;
}
case 'S': {
if (up(p[1]) == 'E' && up(p[2]) == 'P') return 9;
break;
}
}
return -1;
}
Solution based on boost Spirit X3
#include <iostream>
#include <memory>
#include <string.h>
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
namespace x3 = boost::spirit::x3;
struct Month_ : x3::symbols<std::uint8_t>
{
Month_()
{
add
("Jan", 1)
("Feb", 2)
("Mar", 3)
("Apr", 4)
("May", 5)
("Jun", 6)
("Jul", 7)
("Aug", 8)
("Sep", 9)
("Oct", 10)
("Nov", 11)
("Dec", 12);
}
}month_;
using ResultType = std::tuple<int, int, int>;
namespace grammar
{
using namespace x3;
auto const start_ = rule<struct start_, ResultType>{"start"}
= int_ >> month_ >> int_;
};
int main(int athc, char* argv[])
{
std::string str{"25Dec2016"};
auto beg = std::begin(str);
auto end = std::end(str);
ResultType res;
auto ret = x3::parse(beg,end, grammar::start_, res);
if(ret && (beg==end) )
{
std::cout << "parse done :" << std::get<0>(res) << " " << std::get<1>(res) << " " << std::get<2>(res) << "\n";
}
else
{
std::cout << "parse failed '" << std::string(beg, std::next(beg, 10)) << "'\n";
}
return 0;
}
Note, this works also with char*
Live On Coliru
Here are remarks on your code and some possible simplifications:
You can remove the else branches from the error handling if tests that throw an exception. As a matter of fact, you could just return a completion status instead of throwing exceptions.
Structured exceptions would be more precise than just throwing strings, but I'm not a C++ expert.
If the input string is null-terminated after the year part, there is no need to extract the number fields, but you might want to verify correct formatting.
Using an array to match the month part would greatly simplify that part.
Here is a simpler version:
typedef enum ParseStatus {
Parse_OK = 0,
Parse_NullStringPointer,
Parse_InvalidStringFormat,
Parse_InvalidDayNumber,
Parse_InvalidMonthName,
Parse_InvalidYearNumber
} ParseStatus;
ParseStatus Parse(const char *cDate, int &day, int &month, int &year) {
static const char months[4][12] = {
"JAN", "FEB", "MAR", "APR", "MAY", "JUN",
"JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};
static const int maxday[12] = {
31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
char mon[4];
unsigned int dd, mm, yyyy;
char c;
if (!cDate) {
return Parse_NullStringPointer;
}
/* Using sscanf for a simple solution.
* If the string has the correct form, `sscanf` will convert 3 fields.
* extra spaces will be accepted and ignored.
*/
if (sscanf(cDate, "%u%3s%u%c", &dd, mon, &yyyy, &c) != 3) {
return Parse_InvalidStringFormat;
}
/* If you have `strupr(char *);`, you could use it here */
mon[0] = toupper((unsigned char)mon[0];
mon[1] = toupper((unsigned char)mon[1];
mon[2] = toupper((unsigned char)mon[2];
for (mm = 0; mm < 12; mm++) {
if (!strcmp(mon, months[mm]))
break;
}
if (mm >= 12) {
return Parse_InvalidMonthName;
}
/* Further validation on the day number */
if (dd < 1 || dd > 31 || dd > maxday[mm]) {
return Parse_InvalidDayNumber;
}
/* check for leap year, assuming gregorian calendar */
if (dd == 29 && mm == 1 &&
(yyyy % 4 != 0 || (yyyy % 100 == 0 && yyyy % 400 != 0)) {
return Parse_InvalidDayNumber;
}
/* check some limits for the year */
if (yyyy < 1 || yyyy > 9999) {
return Parse_InvalidYearNumber;
}
day = dd;
month = mm + 1;
year = yyyy;
return Parse_OK;
}
If you do not want to use sscanf(), you can use strtol() this way, but it is more cumbersome:
char *p;
int i;
dd = strtol(cDate, &p, 10);
for (i = 0; i < 3 && isalpha((unsigned char)p[i]); i++) {
mon[i] = toupper((unsigned char)p[i]);
}
mon[i] = '\0';
yyyy = strtol(p, &p, 10);
if (*p != '\0') {
return Parse_InvalidStringFormat;
}
When it comes to low level parsing I think std::strtol is your friend because it keeps track of the current pointer position in the string you are parsing. Not that that is critical in this type of string with fixed length components. Also std::strtol has easy error checking.
Also using iterators and algorithms helps to keep things neat and tidy.
This is about as "elegant" as I can make this using low level constructs:
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <iostream>
void parse(const char* cDate, int& day, int& month, int& year)
{
static const char* months[] =
{
"JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"
};
char* pos;
day = std::strtol(cDate, &pos, 10);
if(std::distance(cDate, static_cast<const char*>(pos)) != 2)
throw std::runtime_error("bad date format");
char mon[4] = {};
for(auto m = std::begin(mon); m != std::end(mon) - 1; ++m)
*m = std::toupper(*pos++);
auto found = std::find_if(std::begin(months), std::end(months),
[&](const char* m){ return !std::strcmp(mon, m); });
if(found == std::end(months))
throw std::runtime_error("bad month format");
month = std::distance(months, found) + 1;
char* end;
year = std::strtol(pos, &end, 10);
if(std::distance(pos, end) != 4)
throw std::runtime_error("bad year format");
}
int main()
{
try
{
auto s = "25Dec2016";
int d, m, y;
parse(s, d, m, y);
std::cout << d << "/" << m << "/" << y << '\n';
}
catch(const std::exception& e)
{
std::cerr << e.what() << '\n';
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
N.B. Not heavily tested may contain bugs.

C++ program parser

We are required to parse out a given program code. Example of code:
procedure example1 {
x = 0;
z = y + x;
a =1;
while a{
x = z + x;
while x {
c = a + b;
}
}
}
What I have tried:
The example code is in a text file, so i open it and then i pass the info to a vector, after which, i get the tokens from the vector one by one and analyse it looking for the keyword. Currently, my code keeps displaying the error message in the Error method, and i can't see to understand why. This is a school assignment. My code is given below. Any and all help is appreciated.
vector<string> tokens;
SimpleParser::SimpleParser()
{
cout << "Please enter a file name: ";
cin >> userInput;
cout << "fILENAME: " + userInput;
openFile(userInput);
}
SimpleParser::~SimpleParser()
{
}
void SimpleParser::openFile(string fileName) {
ifstream myfile(fileName);
if (myfile.is_open())
{
while (getline(myfile, currLine))
{
size_t comments = currLine.find("//");
if (comments != string::npos)
{
currLine = currLine.erase(comments);
allLines += " " + currLine;
}
else {
allLines += " " + currLine;
}
}
myfile.close();
fillVector(allLines);
}
else
{
cout << "Unable to open file";
}
}
//check if line is proc, while,assign
void SimpleParser::fillVector(string line) {
istringstream iss(line);
copy(istream_iterator<string>(iss),
istream_iterator<string>(),
back_inserter(tokens));
next_token = getToken();
procedure();
}
void SimpleParser::procedure() {
Match("procedure");
//string proc_name = next_token;
//Match(proc_name);
Match("{");
stmtLst();
Match("}");
}
void SimpleParser::stmtLst() {
cout << "All lines : "+ allLines;
}
void SimpleParser::Match(string token) {
if (next_token.compare(token) == 0) {
next_token = getToken();
}
else {
Error();
}
}
string SimpleParser::getToken() {
string t = "";
if (countOfVecs < tokens.size()) {
t = tokens[countOfVecs];
}
countOfVecs++;
return t;
}
void SimpleParser::Error() {
cout << "Error parsing!";
//exit(0);
}
void SimpleParser::Stmt() {
string var_name = next_token;
Match(var_name);
Match("=");
Match(next_token);
}
As I can see, the problem is related either to your get or your:
void SimpleParser::Match(string token) {
// compare token with next_token
if (next_token.compare(token) == 0){
// if they match assign next_token to the next token
next_token = getToken();
}else{
// if they don't compare equal throw an error
Error();
}
}
What exactly is the purpose of the above function?
In general there is no need to waste so much memory and read all the file, you can parse it word by word till you get the needed key word. Thus, here is a slightly different implementation of class that will actually parse without copying all the file contents.
class Token{
public:
// data members for the three types of tokens:
// algebraic operator, number, and user defined variable(name, value)
char kind;
double value;
string name;
// constructors for each of the three tokens
Token(char ch): kind(ch), value(0) { }
Token(char ch, double val) :kind(ch), value(val) { }
Token(char ch, string n) :kind(ch), name(n) { }
};
// class used as an input stream for tokens
class Token_stream {
public:
// constructor
Token_stream() :full(false), buffer(0) { }
// member functions
Token get();
private:
// data members defining the Token_stream buffer
bool full;
Token buffer;
};
const string firstKeyword = "key1";
// get function implementation
Token Token_stream::get(){
if (full) {
full=false;
return buffer;
}
char ch;
// to read from file change cin to the relevant input stream object
cin.get(ch);
switch (ch){
// if the token some of the above symbols: return it
case '(': case ')': case '+': case '-': case ',': case '!':
case '*': case '/': case '%': case 'Q': case '=':
return Token(ch);
// if the token a number int of float: return it as "number token"
case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
{
cin.putback(ch);
double val;
cin >> val;
return Token(number,val);
}
case '\n': case '\r':
return Token(print);
default:
{
if (isspace(ch)) // skips whitespaces; taking up this funciton from the replaced 'cin'
{
while(isspace(ch)) cin.get(ch);
}
if (isalpha(ch) || ch == '_' || ch == '#') {
string s;
s += ch;
while(cin.get(ch) && (isalpha(ch) || isdigit(ch) || ch == '_' || ch == '#')) s += ch;
cin.putback(ch);
// if the token is some of the predefined "Keywords": return it as the respective token
if (s == firstKeyword) return Token(keyToken);
if (s == secondKeyword) return Token(sekondKeyToken);
if (s == thirdKeyword) return Token(thirdKeyToken);
return Token(name,s);
}
error("bad input token!", ch);
}
}
}

How to include global functions in a separate file

I am trying to organize my code by grouping functions in seperate header/source files. I've #included the header file in my main .cpp, but the compiler does not see the functions in convertTypes.cpp. What gives? And how do I use my 'key' typedef globally (so also in the seperated function sources)? Lots of code, sorry.
/*
* NoteMaker.cpp
*
* Created on: Sep 4, 2013
* Author: edwinrietmeijer
*/
typedef struct {
int keyNum;
int keyType;
} key;
#include <iostream>
#include <string>
#include <iomanip>
#include "convertTypes.h"
using namespace std;
const int KEYSET[ ] = { 0, 2, 4, 5, 7, 8, 9 };
int* generateNotes( int, key );
void echoNoteList( const int* , const int, const key );
string getKeyStringFromUser();
int main() {
key keyStruct;
int octave;
int nrOfNotes;
string firstNoteName;
// Get inputs
cout << "What key would you like to generate notes in? ( f, cis, es, etc.)" << endl;
firstNoteName = getKeyStringFromUser();
cout << "In what octave would you like to generate notes? (-1 / 9)" << endl;
cin >> octave;
octave += 1;
cout << "How many notes do you wish to generate?" << endl;
cin >> nrOfNotes;
// create a key data struct from input string
keyStruct = convertKeyStringToKeyStruct( firstNoteName );
// add the starting octave nr to the keyStruct
keyStruct.keyNum += octave * 12;
// generate note list
int* noteList = new int[ nrOfNotes ];
noteList = generateNotes( nrOfNotes, keyStruct );
// echo note list to terminal
echoNoteList( noteList , nrOfNotes, keyStruct);
cin.get();
}
int* generateNotes( int notes, key keyStruct) {
int* newList = new int [notes];
int currNote = keyStruct.keyNum + keyStruct.keyType;
int currDist = 0;
newList[0] = currNote;
for (int i=1; i < notes; i ++) {
currDist = i % 7;
if ( currDist == 0 || currDist == 3 ) // half step or whole step?
{ currNote = currNote + 1; }
else
{ currNote = currNote + 2; }
newList[ i ] = currNote;
}
cout << "Generated list." << endl;
return newList;
}
void echoNoteList( const int* noteList, const int nrOfNotes, const key thisKeyStruct )
{
int currNote;
for (int i = 0; i < nrOfNotes ; i ++) {
currNote = noteList[ i ] % 12;
if ( currNote < 0 )
currNote += 12;
cout << left;
cout << setw(5) << noteList[ i ] << setw( 5 ) << convertToNoteName( currNote, thisKeyStruct.keyType ) << endl;
}
}
string getKeyStringFromUser() {
bool correctInput = false;
string getKeyName;
int keyNum;
while ( ! correctInput ) {
cin >> getKeyName;
cout << endl;
keyNum = getKeyName[ 0 ];
if ( keyNum > 96 && keyNum < 104 ) {
correctInput = true;
}
else
{
cout << "Wrong input. Try again." << endl;
}
}
return getKeyName;
}
convertTypes.h
#ifdef CONVERTTYPES_H
#define CONVERTTYPES_H
std::string convertToNoteName( int, int );
key convertKeyStringToKeyStruct( std::string );
#endif
convertTypes.cpp
/*
* convertTypes.cpp
*
* Created on: Sep 5, 2013
* Author: edwinrietmeijer
*/
#include <string>
#include "convertTypes.h"
using namespace std;
typedef struct {
int keyNum;
int keyType;
} key;
key convertKeyStringToKeyStruct( string firstNote ) {
int stringSize;
int keyType = 0;
char keyChar;
key thisKey;
keyChar = firstNote[ 0 ];
// get key type (flat, sharp, normal)
stringSize = firstNote.size( );
if (stringSize > 1 ) {
switch( firstNote[ 1 ] ) {
case 'e':
keyType = -1; break;
case 's':
keyType = -1; break;
case 'i':
keyType = 1; break;
default:
keyType = 0; break;
}
}
// convert key char to ascii code
int ASkey = keyChar;
thisKey.keyNum = KEYSET[ ASkey - 99 ];
thisKey.keyType = keyType;
return thisKey;
}
string convertToNoteName( int thisNote, int thisKeyType = 0) {
string noteName;
char addKeyType;
switch( thisKeyType ) {
case -1:
addKeyType = 'b'; break;
case 0:
addKeyType =' '; break;
case 1:
addKeyType = '#'; break;
}
switch( thisNote ) {
case 0:
noteName = "C"; break;
case 1:
if( thisKeyType == 1)
noteName = string ("C") + addKeyType;
else
noteName = string("D") + addKeyType; break;
case 2:
noteName = "D"; break;
case 3:
if( thisKeyType == 1)
noteName = string ("D") + addKeyType;
else
noteName = string("E") + addKeyType; break;
case 4:
noteName = "E"; break;
case 5:
noteName = "F"; break;
case 6:
if( thisKeyType == 1)
noteName = string ("F") + addKeyType;
else
noteName = string("G") + addKeyType; break;
case 7:
noteName = "G"; break;
case 8:
if( thisKeyType == 1)
noteName = string ("G") + addKeyType;
else
noteName = string("A") + addKeyType; break;
case 9:
noteName = "A"; break;
case 10:
if( thisKeyType == 1)
noteName = string ("A") + addKeyType;
else
noteName = string("B") + addKeyType; break;
case 11:
noteName = "B"; break;
default:
noteName = "!"; break;
}
return noteName;
}
Change:
#ifdef CONVERTTYPES_H
to:
#ifndef CONVERTTYPES_H
You are effectively compiling-out your definitions.
As to your second point, move this:
typedef struct {
int keyNum;
int keyType;
} key;
into the header file (before its first use there).
However I would warn against using a name like key as it's commonly used as a variable name. I would go for key_t or MySpecialKeyForStuffImDoing (or somesuch).
In addition to #trojanfor's anwer: also create a new NoteMaker.h containing the key structure definition or move the stuct definition to convertTypes.hso that you don't duplicate it in multiple places