Tic-tac-toe game with Linked List C++ - c++

In one of the c++ books that I am reading, I came across one exercise that proposes me to do a tic-tac-toe game using both linked list and arrays. I decided to try with linked list first, since with arrays is obviously easier. However, I got stuck on how to check if someone won the game. Here's what I have so far:
struct board
{
bool square, circle, empty;
int pos;
board* next;
};
void startGame(int first, board* fullBoard);
board* getBoard(board* fullBoard);
int main()
{
int dice, first;
board* fullBoard = NULL;
cout << "Welcome to Tic-tac-toe DOS Game. (2 Player version)\n\n";
cout << "X is Player 1 and O is Player 2.\nI will decide who is starting in the first match...\n ";
srand(time(NULL));
dice = 1;//rand() % 6 + 1;
cout << dice;
if(dice <= 3)
{
first = 1;
cout << "Player 1 is the first!\n";
}
else
{
first = 2;
cout << "Player 2 is the first!\n\n";
}
system("pause");
system("cls");
startGame(first, fullBoard);
}
void startGame(int first, board* fullBoard)
{
int choice;
bool isPlaying;
for(int i = 1; i <= 9; i++)
fullBoard = getBoard(fullBoard);
bool isGameOn = true;
while(isGameOn == true)
{
board* current = fullBoard;
while(current != NULL)
{
if(current->empty == true)
cout << " " << current->pos;
else if(current->circle == true)
cout << " " << "O";
else
cout << " " << "X";
if( current->pos == 4 || current->pos == 7)
{
cout << "\n";
cout << "-----------------------\n";
}
else if (current->pos == 1)
cout << "\n";
else
cout << " |";
current = current->next;
}
if(first == 1)
{
isPlaying = true;
while(isPlaying == true)
{
cout << "Player 1, please put the number corresponding to the area you want to fill: ";
cin >> choice;
while(choice < 1 || choice > 9)
{
cout << "Invalid choice. Please choose a valid option: ";
cin >> choice;
}
current = fullBoard;
while(current != NULL && current->pos != choice)
current = current->next;
if(current->empty == true)
{
current->empty = false;
current->square = true;
isPlaying = false;
first = 2;
}
else
cout << "The field that you chose is already used...\n";
}
}
else
{
isPlaying = true;
while(isPlaying == true)
{
cout << "Player 2, please put the number corresponding to the area you want to fill: ";
cin >> choice;
while(choice < 1 || choice > 9)
{
cout << "Invalid choice. Please choose a valid option: ";
cin >> choice;
}
current = fullBoard;
while(current != NULL && current->pos != choice)
current = current->next;
if(current->empty == true)
{
current->empty = false;
current->circle = true;
isPlaying = false;
first = 1;
}
else
cout << "The field that you chose is already used...\n";
}
}
system("cls");
}
}
board* getBoard(board* fullBoard)
{
board* newBoard = new board;
newBoard->empty = true;
newBoard->circle = false;
newBoard->square = false;
newBoard->next = fullBoard;
if(newBoard->next != NULL)
newBoard->pos = newBoard->next->pos + 1;
else
newBoard->pos = 1;
return newBoard;
}
As you can see, on my struct Board, I have an int called pos, which I created to keep track of the whole board. The only solution that I can imagine so far, is checking every single position. Ex: compare pos 8 with pos 9, 7, 5 and 2, compare pos 9 with pos 8, 7, 6, 3, 5 and 1. But I think that is way too extensive (and maybe it's hard coding as well?). What other options do you think I have?
Thanks in advance,
Felipe

I need more space to explain the "multiple lists" concept.
Your board:
_ _ _
|_|_|_|
|_|_|_|
|_|_|_|
The lists that represent the possible solutions
D1 A B C D2
_ _ _
E |_|_|_|
F |_|_|_|
G |_|_|_|
Note that each cell will be in at least 2 lists.
Option 1)
You store those lists in a single "list of lists". When a move is done, you iterate that list of lists, and, for each sublist (which would be A, B, etc. from the previous figure), you iterate the cells. If all the cells in a sublist are from the player who moved, you found a winner.
Option 2)
Each cell has a member that is a "list of lists". That "list of lists" holds the lists to check when a move is done in that cell (for example, for cell 1, you would have lists A, E and D1). When a move is done in a cell, you get that list from the cell and check the sublists to see if you got a winner (it is more or less the same than option 1, but limiting the lists that you have to iterate over each time).
Note that in all the cases, we are dealing only with lists (if you add the "pointer to diagonal" and similar the structure no longer is a list). Only that:
there are many lists.
there are lists of cells, and lists of lists of cells.

Related

Jumping into C++: Tic Tac Toe game [using enum] [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 months ago.
Improve this question
First and foremost, I ask that anyone who is replying consider these factors. Currently I have very-low knowledge in arrays and enumerations, most of the basics are understood such as what you will see below. I have been trying to figure this problem out for 3 days and now I'm asking for help before I keep burning myself out every 45mins-1hr.
Problem: Write a two-player tic-tac-toe game, allowing two humans to play against each other; use
enums when possible to represent the values of the board
#include<iostream>
#include<string>
bool tie = false;
std::string p1, p2;
enum turnbased{token1, token2, empty};
char space[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int row;
int column;
int token;
void board() //producing the required space[][] coordinates to empty squares
{
std::cout << " | | \n";
std::cout <<" "<<space[0][0]<<" | "<<space[0][1]<<" | "<<space[0][2]<<" \n";
std::cout << " ____|____|____\n";
std::cout <<" "<<space[1][0]<<" | "<<space[1][1]<<" | "<<space[1][2]<<" \n";
std::cout << " ____|____|____\n";
std::cout << " | | \n";
std::cout <<" "<<space[2][0]<<" | "<<space[2][1]<<" | "<<space[2][2]<<" \n";
std::cout << " | | \n";
}
void playerswitch()
{
turnbased assignto = token1; //initializing enum type to define token1 for turns to occur
int token = empty; //declaring token to equivalate to enum variable{empty} in position()
if(assignto)
{
std::cout << p1 << " choose slot for - " << token1;
assignto = token2;
}
else(assignto == token2);
{
std::cout << p2 << " choose slot for - " << token2;
assignto;
}
std::cin >> token;
}
void position()
{
if(empty == 1) // positioning array space
{
row = 0;
column = 0;
}
else if(empty == 2)
{
row = 0;
column = 1;
}
else if(empty == 3)
{
row = 0;
column = 2;
}
else if(empty == 4)
{
row = 1;
column = 0;
}
else if(empty == 5)
{
row = 1;
column = 1;
}
else if(empty == 6)
{
row = 1;
column = 2;
}
else if(empty == 7)
{
row = 2;
column = 0;
}
else if(empty == 7)
{
row = 2;
column = 1;
}
else if(empty == 8)
{
row = 2;
column = 2;
}
else(empty < 1 || empty > 9);
{
std::cout << "Invalid!!!" << std::endl;
}
//token check and execution
if(token1 && space[row][column] != token1 && space[row][column] != token2)
{
space[row][column] = token1;
token2;
}
else if(token2 && space[row][column] != token1 && space[row][column] != token2)
{
space[row][column] = token2;
token1;
}
else{
std::cout << "Invalid space!" << std::endl;
position();
}
playerswitch();
board();
}
//win check
bool results()
{
for(int i = 0; i < 3; i++)
{ //checks horizontally and vertically
if(space[i][0] == space[i][1] && space[i][0] == space[i][2] || space[0][i] == space[1][i] && space[0][i] == space[2][i])
return true;
//checks diagonally
if(space[0][0] == space[1][1] && space[1][1] == space[2][2] || space[0][2] == space[1][1] && space[1][1] == space[2][0])
return true;
for(int j = 0; j < 3; j++)
{
if(space[i][j] != token1 && space[i][j] != token2)
{
return false;
}
}
}
tie = true;
return false;
}
int main()
{
std::cout << "Enter the name of the first player: \n";
getline(std::cin, p1);
std::cout << p1 << " is player1 so he/she will play first \n";
std::cout << "Enter the name of the second player: \n";
getline(std::cin, p2);
std::cout << p2 << " is player2 so he/she will play second \n";
while(!results())
{
board();
playerswitch();
position();
results();
}
if(token1 && tie)
{
std::cout << p2 << " wins!" << std::endl;
}
else if(token2 && tie)
{
std::cout << p1 << " wins!" << std::endl;
}
else
{
std::cout << "It's a draw!" << std::endl;
}
}```
I wont fix your code, but I will help you structure it so you can figure it and future problems out yourself.
First, get rid of all global variables. This is important because the way to reason about code is to look at individual parts in isolation. This is the only, the ONLY, way writing code scales.
To do this, define an interface and a clear job for each of your functions. Maybe add structs to keep data together that belongs together.
After you have changed the functions to work only on their input arguments (look up how functions receive arguments and return values), you can test each function on its own. Make sure your function to read in input works. Make sure your function to manipulate the playing field works. Do all this before you plug them together. This is called unit testing.
Once you are convinced, that regardless the data that your function receives it always does a proper job, plug them together and see if the result works. If it doesn't, your first goal is to figure out which function is broken. Go back to unit testing with new test inputs.
Rinse and repeat until your program works correctly.

Failing Tictactoe in c++

I'm a beginner in c++, i'm trying to make a tictactoe. My program fails at the part acfter i enter input. When I enter an input, there is no next action from the program like i expected it to ("unvalid", check if win or loose, ask for input again). It only shows blank. like below: after I enter "1", nothing happens, and I can keep enter more input.
terminal example photo
I know it is a simple activity but I just ca't figure it out ToT. Thank you for helping!
//tictactoe
#include <iostream>
#include <vector>
using namespace std;
//declared variables
vector<char> out = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
int in = 2;
char player_out;
bool loose = false;
char x;
bool filled = false;
bool end(){ //won yet?
bool loose = false;
//horizontal
if (out[1] == out[2] && out[3] == out[2]){
loose = true;
}
else if (out[4] == out[5] && out[6] == out[5]){
loose = true;
}
else if (out[7] == out[8] && out[9] == out[8]){
loose = true;
}
//vertical
else if (out[1] == out[4] && out[7] == out[1]){
loose = true;
}
else if (out[2] == out[5] && out[8] == out[2]){
loose = true;
}
else if (out[3] == out[6] && out[9] == out[3]){
loose = true;
}
else if (out[1] == out[5] && out[9] == out[5]){
loose = true;
}
else if (out[3] == out[5] && out[7] == out[5]){
loose = true;
}
else{
loose = false;
}
return loose;
}
void game_start_display(){ //display the board
cout << "TIC TAC TOE\n";
cout << " | | \n";
cout << " " << out[1] << " | " << out[2] << " | " << out[3] << " \n";
cout << "______|______|______\n";
cout << " | | \n";
cout << " " << out[4] << " | " << out[5] << " | " << out[6] << " \n";
cout << "______|______|______\n";
cout << " | | \n";
cout << " " << out[7] << " | " << out[8] << " | " << out[9] << " \n";
cout << " | | \n\n";
}
int change_player(){ //take turn 1st and 2nd player
if (in == 1){
in++;
}
else{
in--;
}
return in;
}
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O'){
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O')
cout << "The square has already been used!\n";
filled = false;
return filled;
}
char player_out_f(){ //change output sign for each players (X, O)
if (in == 1){
player_out = 'X';
}
else if (in == 2){
player_out = 'O';
}
return player_out;
}
void c_player_display(){ //tell players to enter a number
cout << "Player " << in << "'s turn, please enter a number:\n";
}
int main(){
//intro
int loose = false;
game_start_display();
while(loose == false){ //when the game is still happening
change_player(); //change player (player start is set 2 so this comes first and change it to 1)
player_out_f(); //change player output sign (X, O)
c_player_display(); //print the line to ask for input
while(filled == false){ //when the there is no input yet (the spot is not filled)
cin >> x; // input
if (x > 0 && x < 10){ //check if input is in range 1-9
filled_f(); //check if the spot is occupied
}
else if(x < 0 && x > 10) { //if input is out of range
cout << "Invalid! Enter again!\n";
filled = false; //repeat the asking input circle (the while)
}
}
game_start_display(); //output the board again with new char (X or O)
end(); //check if anyone's won yet, if no, repeat the circle
}
cout << "Player " << in << " won! GG";
}
You have infinite loop at while (filled == false) { ... }, because filled_f always sets filled to false (and the else if branch of the condition inside this loop as well does so). It's because you most likely missed figure brackets when writing else if block in filled_f. Your indentation hints that you wanted 2 statements to be in that block, but as of now, only the first is executed conditionally, and the second (filled = false;) is executed after the branch. In other words, with intuitive indentation this function looks like this:
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O') {
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O')
cout << "The square has already been used!\n";
filled = false;
return filled;
}
It sets filled = false; in any case, since if/else execute (depending on condition) the statement immediately following one of them (see here), and indentation is ignored (unlike in, e.g., Python, where indentation alone determines boundaries of conditions, loops, function etc), so only cout << ... is conditionally executed. To do what you want put figure brackets around appropriate statements the same way you already did for the first if branch to create compound statement (block) from them, which is a statement itself and does what you want - groups several other statements within it, executed in sequence:
bool filled_f() { //check if the spot is filled
if (out[x] != 'X' and out[x] != 'O') {
filled = true;
out[x] = player_out; //fill the input into the spot
}
else if (out[x] == 'X' or out[x] == 'O') {
cout << "The square has already been used!\n";
filled = false;
}
return filled;
}
Additional remarks
Note that logically it's not needed to have if condition in else since if the first if condition is dissatisfied, else if condition is definitely satisfied (look De Morgan's Laws) and you can just write else:
// ...
else {
cout << "The square has already been used!\n";
filled = false;
}
// ...
Also in your main loop, you use this:
if (x > 0 && x < 10){ //check if input is in range 1-9
filled_f(); //check if the spot is occupied
}
else if(x < 0 && x > 10) { //if input is out of range
cout << "Invalid! Enter again!\n";
filled = false; //repeat the asking input circle (the while)
}
to test whether x is within range, but your condition in else if is wrong (should be x <= 0 || x >= 10 instead of x < 0 && x > 10) and can be omitted altogether (again see De Morgan's Laws) by using just else.

Inserting new score into sorted array while updating separate array of names

score is an array of 10 scores in ascending order.
scoreName is an array of names associated with with each score.
I need to check if a new score is good enough to enter the the high score table and if so, insert it into the correct position and make sure scoreName is also updated to reflect any changes.
I am having problems with my current code:
//Table
for (int i = 0; i < leaderBoardSize; i++)
{
cout << scoreName[i] << "\t\t" << score[i] << endl;
}
system("pause");
//Check to see if you made the highScore
if (diceTotal >= score[9])
{
cout << "Congrats, you have made the HighScore Table !\nEnter Your Name.";
cin >> playerName;
for (int i = 9; i < leaderBoardSize; i--)
{
if (diceTotal <= score[i])
{
scoreName[i = i + 1] = scoreName[i];
score[i = i + 1] = score[i];
scoreName[i] = playerName;
score[i] = diceTotal;
break;
}
scoreName[i = i + 1] = scoreName[i];
score[i = i + 1] = score[i];
}
}
Here is the entire code:
#include <iostream>
#include <string>
#include <time.h>
using namespace std;
int main()
{
//dice game variables
int dice1 = 0;
int dice2 = 0;
int diceTotal = 0;
int round = 0;
string choice;
bool isDone = true;
//Scoreboard
const int leaderBoardSize = 10;
int score[10] = { 40, 33, 29, 24, 22, 19, 15, 12, 11, 9 };
string scoreName[leaderBoardSize] = { "Jason", "Steve", "Bob", "Timberduck", "Eric", "Susan", "Tyler", "Nick", "NinjaDave", "RaidenGunfire" };
string playerName;
//random number seeder
srand((unsigned int)time(NULL));
//Game instructions
cout << "dice game\n---------\nCreated By: Darcy Tellier\n--------------------------\nInstructions:\nRoll 2 dices. Try to get as close to 50 without going over." << endl;
//The Game loop
do
{
//resets game variables
diceTotal = 0;
round = 1;
//in game match loop
do
{
// display round #, current dice total, ask user to quit or re-roll.
cout << "Round\n-----\n " << round << endl;
cout << "current total:" << diceTotal << endl;
cout << "Roll dice (y/n)?";
cin >> choice;
//Checks the users imput for invalid characters
while (choice != "Y" && choice != "y" && choice != "N" && choice != "n")
{
cout << "invalid option. Choose y/n:" << endl;
cin >> choice;
}
if (choice == "Y" || choice == "y")
{
//roll dice
round += 1;
dice1 = rand() % 6 + 1;
dice2 = rand() % 6 + 1;
diceTotal = diceTotal + dice1 + dice2;
cout << "you have rolled a " << dice1 << " and a " << dice2 << endl;
if (diceTotal > 50)
{
isDone = false;
}
}
else
{
//break was used because "isDone = false" does not work here. The debugger shows that when the variable is set to false, it still ignores it and skips to the next round.
break;
}
} while (isDone == true || diceTotal < 50);
//end of round
if (diceTotal > 50)
{
cout << "\nGameOver" << endl;
cout << "You went over in " << round << " turns. You Lose!!! " << endl;
}
else
{
cout << "You stopped at " << round << " turns. Final score: " << diceTotal << "." << endl;
system("pause");
system("cls");
}
//Table
for (int i = 0; i < leaderBoardSize; i++)
{
cout << scoreName[i] << "\t\t" << score[i] << endl;
}
system("pause");
//Check to see if you made the highScore
if (diceTotal >= score[9])
{
cout << "Congrats, you have made the HighScore Table !\nEnter Your Name.";
cin >> playerName;
for (int i = 9; i < leaderBoardSize; i--)
{
if (diceTotal <= score[i])
{
scoreName[i] = playerName;
score[i] = diceTotal;
break;
}
}
}
//board display #2
for (int i = 0; i < leaderBoardSize; i++)
{
cout << scoreName[i] << "\t\t" << score[i] << endl;
}
system("pause");
//do you want to play again?
cout << "Do you want to play again";
cin >> choice;
while (choice != "Y" && choice != "y" && choice != "N" && choice != "n")
{
cout << "invalid option. Choose y/n:" << endl;
cin >> choice;
}
if (choice == "Y" || choice == "y")
{
system("cls");
isDone = true;
}
else
{
cout << "game over" << endl;
isDone = false;
}
} while (isDone);
system("pause");
return 0;
}
This is a copy of the assignment.
Note: I am not asking for you guys to do my work. I just want to figure out the highScore sorting thing.
The problem: arrays are not a good way to achieve what you want
You are trying to see if a score beat another score, and if so, replace that and move the rest of the scores down. I assume your scores are sorted, and that score is a int[10], but you have several problems:
1.
for (int i = 9; i < leaderBoardSize; i--)
You are attempting to iterate through your scores backwards, so you start at 9 (which I assume is the last index) and work your way down. Assuming leaderBoardSize is the total size of the leaderboard, and likely 10, i will always be less than leaderBoardSize and your for loop will go for a loooong time. You probably meant to say:
for (int i = 9; i >= 0; i--)
2.
scoreName[i = i + 1] = scoreName[i];
score[i = i + 1] = score[i];
This is assigning i to a new value, which will also ruin your for loop. You should only be doing
scoreName[i + 1] = scorename[i];
Trying to swap all the values in an array in cumbersome, and you are trying to do everything manually, so I give you:
The solution: use the standard library!
C++ is a great language, if for no other reason than containing a standard library: a library of functions and classes that solve many basic problems for you.
It is far easier to sort, insert and remove elements by using a standard container. Let's use std::vector:
// Simple class to handle a player score: a name and score
struct PlayerScore
{
std::string name;
unsigned int score;
};
// Create some test scores. I assume you have another means of storing high scores, perhaps in a file, but for this small purpose I am just hard-coding some scores:
std::vector<PlayerScore> hiScores = { {"ABC", 5000}, {"XJK", 10000}, {"FOO", 20000}, {"EGG", 4000}, {"HI", 50000} };
You may keep your scores sorted, but if, like mine they aren't sorted, you can sort them easily with std::sort:
std::sort(hiScores.begin(), hiScores.end(), [](PlayerScore ps1, PlayerScore ps2){ return ps1.score > ps2.score; });
With that out the way, you can proceed to play your game and obtain a name for the player and their score. I haven't bothered, and will just create a value for the score:
auto score = 10123u;
// Check if this score beats any of the current high scores:
auto it = std::find_if(hiScores.begin(), hiScores.end(), [score](PlayerScore ps){ return score > ps.score; });
if (it != hiScores.end())
{
// Yes! We beat a score!
std::cout << "Found score: " << it->score << std::endl;
// Insert this score before the other score
hiScores.insert(it, {"NewScore", score});
// Remove the last score:
hiScores.pop_back();
}
You no longer need to iterate and manually try to manipulate the positions of scores. Standard containers with random access such as std::vector allow you to just insert and access elements as you need to. Your 10 lines of code simply becomes 2 lines. std::vector also manages the size for you, so if you only have 3 high scores initially, it can grow easily to contain 10, 100 or many more scores.
std::find_if can find an element in your container without the need to manually iterate over every element. A predicate is passed in which acts as a find condition, which in our case we pass our score and check if it's greater than any PlayerScore in the container. The first element it's greater than is returned to us, and then we can insert our score in front of it, then remove the last score via pop_back
and if so, insert it into the correct position and make sure scoreName is also updated
This is a poor design. If scores and names need to stay together, you should make them a single entity. Define a struct or class to hold a name and associated score, and then store instances of that entity in a single array.

Issues With Displaying Queue Contents

First off I will explain the classes and their purposes: ClinicQueue(Queue class, designed to create queues of patients), ClinicNode(typical node class, necessary for Queue), ClinicPatient(stores data about the patients in the queues), and ClinicDriver(used to construct the main application). What I need to do is to display the queue's contents - each node has a data member "info" which is an instance of ClinicPatient and contains a patient's data. In my current code I can only get one patient to print and I know there's is something wrong with my logic, but after 3 hours of the same issue and various attempts at working around it I just can't see what that problem is. How can I fix my code, or write new code to perform the necessary task? Below is the display function as well as the function intended to grab information about patients.
ClinicPatient ClinicQueue::getInfo(int pos) //get information on patients
{
ClinicNode* Current = front;
for(int i = 0; i < pos; i++)//for pos +/- 1 only outputs
//"patient 1: "(a cout in driver)
{
Current = Current -> next;
}
return Current -> info; //blows up
}
And the display function:
void ClinicDriver::Peekaboo() //breaks with more than one patient, skips last patient
{
bool cont = true;
int QueueChoice;
while (cont == true)
{
string temp;
cout << "Select a Doctor by number." << endl;
cin >> QueueChoice;
if (status[QueueChoice - 1] == true)
{
cout << "Queue is open." << endl;
string OutputS = "";
for (int a = 1; a <= Clinic[QueueChoice - 1].getSize(); a++)
{
cout << "Patient " << a << " : " << endl; //new, debugging
OutputS += Clinic[QueueChoice - 1].getInfo(a-1).tostring();//breaks here, works on 1 patient
OutputS += "\n";
}
cout << OutputS << endl;
}
else
{
cout << "Queue is empty." << endl;
}
cout << "Do you wish to continue?" << endl;
cin >> temp;
if (temp == "Yes" || temp == "yes")
{
cont = true;
}
else
{
cont = false;
}
}
}
Any and all help is greatly appreciated.

Reading input from a text file, omits the first and adds a nonsense value to the end?

When I input locations from a txt file I am getting a peculiar error where it seems to miss off the first entry, yet add a garbage entry to the end of the link list (it is designed to take the name, latitude and longitude for each location you will notice). I imagine this to be an issue with where it starts collecting the inputs and where it stops but I cant find the error!! It reads the first line correctly but then skips to the next before adding it because during testing for the bug it had no record of the first location Lisbon though whilst stepping into the method call it was reading it. Very bizarre but hopefully someone knows the issue. Here is firstly my header file:
#include <string>
struct locationNode
{
char nodeCityName [35];
double nodeLati;
double nodeLongi;
locationNode* Next;
void CorrectCase() // Correct upper and lower case letters of input
{
int MAX_SIZE = 35;
int firstLetVal = this->nodeCityName[0], letVal;
int n = 1; // variable for name index from second letter onwards
if((this->nodeCityName[0] >90) && (this->nodeCityName[0] < 123)) // First letter is lower case
{
firstLetVal = firstLetVal - 32; // Capitalise first letter
this->nodeCityName[0] = firstLetVal;
}
while(n <= MAX_SIZE - 1)
{
if((this->nodeCityName[n] >= 65) && (this->nodeCityName[n] <= 90))
{
letVal = this->nodeCityName[n] + 32;
this->nodeCityName[n] = letVal;
}
n++;
}
//cityNameInput = this->nodeCityName;
}
};
class Locations
{
private:
int size;
public:
Locations(){
}; // constructor for the class
locationNode* Head;
//int Add(locationNode* Item);
};
And here is the file containing main:
// U08221.cpp : main project file.
#include "stdafx.h"
#include "Locations.h"
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
int n = 0,x, locationCount = 0, MAX_SIZE = 35;
string cityNameInput;
char targetCity[35];
bool acceptedInput = false, userInputReq = true, match = false, nodeExists = false;// note: addLocation(), set to true to enable user input as opposed to txt file
locationNode *start_ptr = NULL; // pointer to first entry in the list
locationNode *temp, *temp2; // Part is a pointer to a new locationNode we can assign changing value followed by a call to Add
locationNode *seek, *bridge;
void setElementsNull(char cityParam[])
{
int y=0, count =0;
while(cityParam[y] != NULL)
{
y++;
}
while(y < MAX_SIZE)
{
cityParam[y] = NULL;
y++;
}
}
void addLocation()
{
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
if(!userInputReq) // bool that determines whether user input is required in adding the node to the list
{
cout << endl << "Enter the name of the location: ";
cin >> temp->nodeCityName;
temp->CorrectCase();
setElementsNull(temp->nodeCityName);
cout << endl << "Please enter the latitude value for this location: ";
cin >> temp->nodeLati;
cout << endl << "Please enter the longitude value for this location: ";
cin >> temp->nodeLongi;
cout << endl;
}
temp->Next = NULL; //set to NULL as when one is added it is currently the last in the list and so can not point to the next
if(start_ptr == NULL){ // if list is currently empty, start_ptr will point to this node
start_ptr = temp;
}
else
{ temp2 = start_ptr;
// We know this is not NULL - list not empty!
while (temp2->Next != NULL)
{
temp2 = temp2->Next; // Move to next link in chain until reach end of list
}
temp2->Next = temp;
}
++locationCount; // increment counter for number of records in list
if(!userInputReq){
cout << "Location sucessfully added to the database! There are " << locationCount << " location(s) stored" << endl;
}
}
void populateList(){
ifstream inputFile;
inputFile.open ("locations.txt", ios::in);
userInputReq = true;
temp = new locationNode; // declare the space for a pointer item and assign a temporary pointer to it
do
{
inputFile.get(temp->nodeCityName, 35, ' ');
setElementsNull(temp->nodeCityName);
inputFile >> temp->nodeLati;
inputFile >> temp->nodeLongi;
setElementsNull(temp->nodeCityName);
if(temp->nodeCityName[0] == 10) //remove linefeed from input
{
for(int i = 0; temp->nodeCityName[i] != NULL; i++)
{
temp->nodeCityName[i] = temp->nodeCityName[i + 1];
}
}
addLocation();
}
while(!inputFile.eof());
userInputReq = false;
cout << "Successful!" << endl << "List contains: " << locationCount << " entries" << endl;
cout << endl;
inputFile.close();
}
bool nodeExistTest(char targetCity[]) // see if entry is present in the database
{
match = false;
seek = start_ptr;
int letters = 0, letters2 = 0, x = 0, y = 0;
while(targetCity[y] != NULL)
{
letters2++;
y++;
}
while(x <= locationCount) // locationCount is number of entries currently in list
{
y=0, letters = 0;
while(seek->nodeCityName[y] != NULL) // count letters in the current name
{
letters++;
y++;
}
if(letters == letters2) // same amount of letters in the name
{
y = 0;
while(y <= letters) // compare each letter against one another
{
if(targetCity[y] == seek->nodeCityName[y])
{
match = true;
y++;
}
else
{
match = false;
y = letters + 1; // no match, terminate comparison
}
}
}
if(match)
{
x = locationCount + 1; //found match so terminate loop
}
else{
if(seek->Next != NULL)
{
bridge = seek;
seek = seek->Next;
x++;
}
else
{
x = locationCount + 1; // end of list so terminate loop
}
}
}
return match;
}
void deleteRecord() // complete this
{
int junction = 0;
locationNode *place;
cout << "Enter the name of the city you wish to remove" << endl;
cin >> targetCity;
setElementsNull(targetCity);
if(nodeExistTest(targetCity)) //if this node does exist
{
if(seek == start_ptr) // if it is the first in the list
{
junction = 1;
}
if(seek != start_ptr && seek->Next == NULL) // if it is last in the list
{
junction = 2;
}
switch(junction) // will alter list accordingly dependant on where the searched for link is
{
case 1:
start_ptr = start_ptr->Next;
delete seek;
--locationCount;
break;
case 2:
place = seek;
seek = bridge;
delete place;
--locationCount;
break;
default:
bridge->Next = seek->Next;
delete seek;
--locationCount;
break;
}
}
else
{ cout << targetCity << "That entry does not currently exist" << endl << endl << endl;
}
}
void searchDatabase()
{
char choice;
cout << "Enter search term..." << endl;
cin >> targetCity;
if(nodeExistTest(targetCity))
{
cout << "Entry: " << endl << endl;
}
else
{
cout << "Sorry, that city is not currently present in the list." << endl << "Would you like to add this city now Y/N?" << endl;
cin >> choice;
/*while(choice != ('Y' || 'N'))
{
cout << "Please enter a valid choice..." << endl;
cin >> choice;
}*/
switch(choice)
{
case 'Y':
addLocation();
break;
case 'N':
break;
default :
cout << "Invalid choice" << endl;
break;
}
}
}
void printDatabase()
{
temp = start_ptr; // set temp to the start of the list
do
{ if (temp == NULL)
{
cout << "You have reached the end of the database" << endl;
}
else
{ // Display details for what temp points to at that stage
cout << "Location : " << temp->nodeCityName << endl;
cout << "Latitude : " << temp->nodeLati << endl;
cout << "Longitude : " << temp->nodeLongi << endl;
cout << endl;
// Move on to next locationNode if one exists
temp = temp->Next;
}
}
while (temp != NULL);
}
void nameValidation(string name)
{
n = 0; // start from first letter
x = name.size();
while(!acceptedInput)
{
if((name[n] >= 65) && (name[n] <= 122)) // is in the range of letters
{
while(n <= x - 1)
{
while((name[n] >=91) && (name[n] <=97)) // ERROR!!
{
cout << "Please enter a valid city name" << endl;
cin >> name;
}
n++;
}
}
else {
cout << "Please enter a valid city name" << endl;
cin >> name;
}
if(n <= x - 1)
{
acceptedInput = true;
}
}
cityNameInput = name;
}
int main(array<System::String ^> ^args)
{
//main contains test calls to functions at present
cout << "Populating list...";
populateList();
printDatabase();
deleteRecord();
printDatabase();
cin >> cityNameInput;
}
The text file contains this (ignore the names, they are just for testing!!):
Lisbon 45 47
Fattah 45 47
Darius 42 49
Peter 45 27
Sarah 85 97
Michelle 45 47
John 25 67
Colin 35 87
Shiron 40 57
George 34 45
Sean 22 33
The output omits Lisbon, but adds on a garbage entry with nonsense values. Any ideas why? Thank you in advance.
The main function creates a new locationNode and stores it in the global variable temp, then reads the first dataset and stores it in that node.
Then you call addLocation() which starts by creating another new locationNode which replaces the existing one in temp. This new node is then inserted into the list.
The next iteration of the main loop then fills that temp value with values and addLocation() inserts another brand new locationNode into the list. So the first dataset isn't stored in the list and each iteration ends up by inserting an uninitialized new node.
As you see using global variables can lead to confusing situations and you code would surely become clearer if you were passing the nodes as parameters to your functions.