I'm working on a program to aid me in world-building that randomly generates a settlement (hamlet, village, town, city) based on a nation (German, Latin, Eastern) that the user chooses. I've integrated a sort of settler generation system to create settlers within the settlement, each with a name, age, gender, and wealth using a constructor and holding the results as objects within a vector. Unfortunately, the program creates an entire population of clones, filling the vector with settlers with the same name, age, etc.
I've tried initialising the Settler class' constructor in a for loop, but that hasn't changed anything except causing you to get a different set of settlers each time you request information on one of them.
Settler Constructor:
class Settler {
public:
int settlerAge;
string settlerName;
string settlerGender;
string settlerWealth;
Settler(int type, int nation, int quantity) {
int result{};
string givenName{};
string surName{};
// Latin Male First Name
string latinMaleName[15] = {"Faustus", "Mamercus", "Mettius", "Appius", "Hostus", "Quintus", "Cossus", "Secundus", "Servius", "Gallio", "Tettienus", "Petronius", "Paesentius", "Pescunnius", "Clodius"};
// Latin Surname
string latinSurname[30] = {"Natalinus", "Lucilianus", "Crispian", "Laetinianus", "Falco", "Otho", "Plautius", "Pascentius", "Lepidus", "Moderatus", "Caeparius", "Caetronius", "Hostilius", "Aedinius", "Papius", "Gennadia", "Triaria", "Planta","Amantia", "Mico", "Opilio", "Augusta", "Laevina", "Longina", "Mico", "Servana", "Sicula", "Iovina", "Albana", "Concessa"};
// Latin Female First Name
string latinFemaleName[15] = {"Vorenia", "Tuccia", "Consentia", "Vinicia", "Aurelia", "Helvia", "Fabia", "Aemilia", "Petilia", "Cloelia", "Viducia", "Betiliena", "Sornatia", "Memmia", "Pedia"};
// As above, so below...
string germanMaleName[15] = {"Carsten", "Benedikt", "Emmerich", "Tillmann", "Maik", "Severin", "Adrian", "Gregor", "Ingolf", "Germund", "Adelmar", "Eckard", "Raimund", "Marwin", "Dietmar"};
string germanSurname[30] = {"Loeb", "Spielberg", "Lindemann", "Frenz", "Buxbaum", "Macher", "Bacharach", "Homrighausen", "Faulhaber", "Herder", "Germar", "Eisen", "Hackl", "Specht", "Rossmann", "Erdmann", "Osterhaus", "Steinsaltz", "Spiegelmann", "Lindemann", "Kluckhohn", "Kuttner", "Seelmann", "Sattler", "Kautner", "Dunhaupt", "Scharf", "Preisner", "Werthner", "Breitner"};
string germanFemaleName[15] = {"Selina", "Isabel", "Walburg", "Berta", "Kate", "Gisela", "Amelie", "Ronja", "Karin", "Lena", "Alexandra", "Sarah", "Monica", "Kai", "Nadja"};
// NOTE: Surname is before given name in eastern nations
string easternMaleName[15] = {"Nikki", "Moronobu", "Yaichiro", "Shuncho", "Tamasaburo", "Sekien", "Kazutoshi", "Yasuhide", "Omezo", "Kinzo", "Junji", "Utamuro", "Hisaki", "Taki", "Mitsuo"};
string easternSurname[30] = {"Maki", "Shinohara", "Tsukino", "Ikeda", "Matsutoya", "Sakata", "Horiuchi", "Suda", "Tsuga", "Kawano", "Kanbayashi", "Kirigaya", "Sakimoto", "Urushido", "Inaba", "Tsukiyomi", "Saeki", "Soga", "Morioka", "Yamabe", "Nakajima", "Maruyama", "Suga", "Kamino", "Kawamoto", "Takanashi", "Ito", "Kuramoto", "Maeda", "Kanemaru"};
string easternFemaleName[15] = {"Saito", "Ane", "Fuuko", "Taji", "Isoko", "Gen", "Kuwa", "Taira", "Sachi", "Uka", "Ryoko", "Hina", "Mitsu", "Asa", "Tomie"};
// Gender Generation (Male/Female)
result = (1 + (rand() % 2));
if (result > 1) {
settlerGender = "female";
} else {
settlerGender = "male";
}
// Name and Age Generation (See Arrays)
result = (rand() % 15);
switch(nation) {
case 1: // Latin (Equal split of ages)
if (settlerGender == "male") {
givenName = latinMaleName[result];
} else {
givenName = latinFemaleName[result];
}
result = (1 + (rand() % 100));
if (result >= 66) {
settlerAge = (41 + (rand() % 30));
} else if (result > 33 || result < 66) {
settlerAge = (22 + (rand() % 41));
} else {
settlerAge = (1 + (rand() % 22));
}
result = (rand() % 30);
surName = latinSurname[result];
settlerName = givenName + " " + surName;
break;
case 2: // German (More young people)
if (settlerGender == "male") {
givenName = germanMaleName[result];
} else {
givenName = germanFemaleName[result];
}
result = (1 + (rand() % 100));
if (result >= 90) {
settlerAge = (41 + (rand() % 30));
} else if (result > 40 || result < 90) {
settlerAge = (22 + (rand() % 41));
} else {
settlerAge = (1 + (rand() % 22));
}
result = (rand() % 30);
surName = germanSurname[result];
settlerName = givenName + " " + surName;
break;
case 3: // Eastern (More older people)
if (settlerGender == "male") {
givenName = easternMaleName[result];
} else {
givenName = easternFemaleName[result];
}
result = (1 + (rand() % 100));
if (result >= 40) {
settlerAge = (41 + (rand() % 30));
} else if (result > 20 || result < 40) {
settlerAge = (22 + (rand() % 41));
} else {
settlerAge = (1 + (rand() % 22));
}
result = (rand() % 30);
surName = easternSurname[result];
settlerName = surName + " " + givenName;
break;
}
// Wealth Generation (Poor/Decent/Rich: Based on Surname)
switch(type){
case 1: // Hamlet
if (result >= 28) {
settlerWealth = "Rich";
} else if (result > 24 || result < 28) {
settlerWealth = "Decent";
} else {
settlerWealth = "Poor";
}
break;
case 2: // Village
if (result >= 26) {
settlerWealth = "Rich";
} else if (result > 19 || result < 26) {
settlerWealth = "Decent";
} else {
settlerWealth = "Poor";
}
break;
case 3: // Town
if (result >= 25) {
settlerWealth = "Rich";
} else if (result > 9 || result < 25) {
settlerWealth = "Decent";
} else {
settlerWealth = "Poor";
}
break;
case 4: // City
if (result >= 20) {
settlerWealth = "Rich";
} else if (result > 9 || result < 20) {
settlerWealth = "Decent";
} else {
settlerWealth = "Poor";
}
break;
}
}
};
Vector Initialisation and Output:
Settlement objSettlement(tempType,tempNation);
int tempNum = objSettlement.settlementQuantity;
std::vector<Settler> objSettler(objSettlement.settlementQuantity, Settler(tempType, tempNation, tempNum));
cout << "Welcome to the " << objSettlement.settlementType << " " << objSettlement.settlementName << " in the " << objSettlement.settlementNation << " nation!\n";
cout << "Featuring a population of " << objSettlement.settlementQuantity << " with " << objSettlement.settlementWealth << " wealth.\n\n";
bool repeat = true;
while (repeat == true) {
cout << "Enter the ID of a settler you would like to read about (from 0-" << (objSettlement.settlementQuantity - 1) << ", or enter 999999 to exit): ";
cin >> tempNum;
cout << "\n\n";
if (tempNum == 999999) {
repeat = false;
} else {
cout << objSettler[tempNum].settlerName << ", " << objSettler[tempNum].settlerGender << ", is " << objSettler[tempNum].settlerAge << " years old and has " << objSettler[tempNum].settlerWealth << " wealth.\n\n";
}
}
For context, "objSettlement" refers to a randomly generated Settlement instance and ".settlementQuantity" is an integer representing the settlement's population.
So, when I create a latin hamlet with 30 people, the program creates a vector with 30 cases of: "Faustus Crispian, male, is 41 years old and has Poor wealth." regardless of what number I put in from 0-29.
Why is my vector initialisation making all objects in my array the same?
This :
std::vector<Settler> objSettler(objSettlement.settlementQuantity, Settler(tempType, tempNation, tempNum));
creates a vector with objSettlement.settlementQuantity copies of the same object Settler(tempType, tempNation, tempNum).
Refer to this reference page for details on how the various vector constructors work.
Something like this might be closer to what you want :
std::vector<Settler> objSettler;
for (int i = 0; i < objSettlement.settlementQuantity; ++i) {
objSettler.emplace_back(tempType, tempNation, tempNum);
}
When you create your objSettler vector, you create one Settler randomly, which will get copied objSettlement.settlementQuantity times. In other words, your constructor is called only once and the instances in the vector are created from that one settler object using the default copy constructor.
See std::vector
For generating n random settlers, you might want to use std::generate_n and std::back_inserter:
std::vector<Settler> objSettler;
std::generate_n(std::back_inserter(objSettler),
objSettlement.settlementQuantity,
[&](){ return Settler(tempType, tempNation, tempNum); });
Edit #2: Ok, I did another *.cpp to check if the codes for Arrow Keys were right. Doing that, I noticed keyPressed variable in detectKeyPressing() had the wrong type of variable, so I changed it from char to int and changed the codes.
Once I did that, it worked. Now I have put the limits, so the Player cannot go outside the square. But I have another problem, the movement is too tough and if you press the keys too fast, the instructions run with a annoying delay. I know I should use either Sleep(ms) or Delay(ms), but I don't know when I should use it.
This is the new code:
#include <iostream>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <Windows.h>
using namespace std;
int detectKeyPressing() {
// 0: Escape
// 1: Enter
//2: Up
// 3: Left
// 4: Down
// 5: Right
int keyPressed = 0;
while (keyPressed != 27) {
if (keyPressed == 0 || keyPressed == 224) {
keyPressed = _getch(); //First value of _getch() when any of the arrow keys are pressed is "224", the next one is the code depending of which arrow you pressed
}
else if (keyPressed == 13) {
return 1;
}
else {
switch (keyPressed) {
//Up
case 72:
return 2;
break;
//Left
case 75:
return 3;
break;
//Down
case 80:
return 4;
break;
//Right
case 77:
return 5;
break;
//Default
default:
return -1;
break;
}
}
};
return 0;
};
int mainMenu() {
int enterPressed = 0;
cout << "Press Enter to Begin, or ESC to exit" << endl;
enterPressed = detectKeyPressing();
system("cls");
return enterPressed;
};
void draw(int playerX, int playerY) {
//Player coordinates, made for testing
cout << "Player.x = " << playerX << endl << "Player.y = " << playerY << endl;
//The next 8 spaces go blank
for (int i = 1; i < 8; i++) {
cout << endl;
}
//Square Limit Making
//Top Limit
for (int iw = 1; iw < 80; iw++) {
cout << "-";
}
cout << endl;
//Border limits and inside the Square
for (int ih = 1; ih < 30; ih++) {
//Left border
cout << "|";
//Inside the Square
for (int iw = 1; iw < 78; iw++) {
if (iw == playerX && ih == playerY) {
cout << "a"; //This is supposed to be ♥ but I don't know how to put it in the screen with a cout
}
else {
cout << " ";
}
}
//Right border
cout << "|" << endl;
}
//Bottom limit
for (int iw = 1; iw < 80; iw++) {
cout << "-";
}
}
int main() {
//Hide cursor
HANDLE hCon;
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
cci.dwSize = 1;
cci.bVisible = FALSE;
SetConsoleCursorInfo(hCon, &cci);
//Variable Making
int gameStarted = -1; // 1 if game is running, 0 if not
//int t = 0; //Turn Counter, not useful for now
Sleep(200); //Wait to get a new seed
srand(time(NULL)); //Seed for rand()
//Menu Loop, remember, 1 if game starts running, 0 if you exit
while (gameStarted > 1 || gameStarted < 0) {
gameStarted = mainMenu();
}
//Like Void Start() in Unity
if (gameStarted == 1) {
int pressedKey = -1; //Creating pressedKey at Start
class Player {
public:
int life = 20;
int accuracy = 80 + (rand() % 100) / 20;
int damage = 5 + (accuracy / 10) + (rand() % 100) / 50;
bool isAlive = true;
int x = 39;
int y = 24;
int speed = 1;
};
class Enemy {
public:
int life = 100;
int satisfaction = 0;
bool isAlive = true;
bool isSatisfied = false;
int damage = 2 + (rand() % 100) / 20;
};
Player Player;
Enemy Enemy;
draw(Player.x, Player.y);
//Like Void Update() in Unity
while (gameStarted != 0) {
pressedKey = detectKeyPressing(); // Save detectKeyPressing()'s return in pressedKey
//Draw if proyectile is moving - not yet
//Draw if player is moving (pay attention specially to this part)
if (pressedKey == 0) {
gameStarted = 0; //if ESC is pressed, exit the loop and exits
}
//If any of the Arrow Keys are pressed
else if (pressedKey > 1 && pressedKey < 6) {
switch (pressedKey) {
//Up
case 2:
Sleep(200);
if (Player.y == Player.speed) {
Player.y = Player.speed; //Top Limit
}
else {
Player.y -= Player.speed;
}
break;
//Left
case 3:
Sleep(200);
if (Player.x == Player.speed) {
Player.x = Player.speed; //Left Limit
}
else {
Player.x -= Player.speed;
}
break;
//Down
case 4:
Sleep(200);
if (Player.y == 30 - Player.speed) {
Player.y = 30 - Player.speed; //Bottom Limit
}
else {
Player.y += Player.speed;
}
break;
//Right
case 5:
Sleep(200);
if (Player.x == 78 - Player.speed) {
Player.x = 78 - Player.speed; //Right Limit
}
else {
Player.x += Player.speed;
}
break;
};
system("cls"); //Erase all
draw(Player.x, Player.y); //Redraw everything, with Player.x or Player.y modified
};
};
};
return 0;
};
Edit #1: I fixed the mistakes you told me, here's the main function modified. It isn't working though.
int main(){
//Hide cursor
HANDLE hCon;
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
cci.dwSize = 50;
cci.bVisible = FALSE; //Changed "TRUE" to "FALSE"
SetConsoleCursorInfo(hCon, &cci);
//Variable Making
int gameStarted = -1; // 1 if game is running, 0 if not
//int t = 0; //Turn Counter, not useful for now
Sleep(200); //Wait to get a new seed
srand(time(NULL)); //Seed for rand()
//Menu Loop, remember, 1 if game starts running, 0 if you exit
while (gameStarted > 1 || gameStarted < 0) {
gameStarted = mainMenu();
}
//Like Void Start() in Unity
if (gameStarted == 1) {
int pressedKey = -1; //Creating pressedKey at Start
class Player {
public:
int life = 20;
int accuracy = 80 + (rand() % 100) / 20;
int damage = 5 + (accuracy / 10) + (rand() % 100) / 50;
bool isAlive = true;
int x = 39;
int y = 24;
int speed = 2;
};
class Enemy {
public:
int life = 100;
int satisfaction = 0;
bool isAlive = true;
bool isSatisfied = false;
int damage = 2 + (rand() % 100) / 20;
};
Player Player;
Enemy Enemy;
draw(Player.x, Player.y);
//Like Void Update() in Unity
while (gameStarted != 0) {
pressedKey = detectKeyPressing(); //Save detectKeyPressing()'s return in pressedKey
//Draw if proyectile is moving - not yet
//Draw if player is moving (pay attention specially to this part)
if (pressedKey == 0) {
gameStarted = 0; //if ESC is pressed, exit the loop and exits
}
//If any of the Arrow Keys are pressed
else if (pressedKey > 1 && pressedKey < 6) {
cout << "There's no problem in Else If statement"; //Couts made for testing
switch (pressedKey) {
cout << "There's no problem in Switch statement";
//Up
case 2:
Player.y -= Player.speed;
cout << "You moved Up";
break;
//Left
case 3:
Player.x -= Player.speed; //Fixed Left movement
cout << "You moved Left";
break;
//Down
case 4:
Player.y += Player.speed;
cout << "You moved Down";
break;
//Right
case 5:
Player.x += Player.speed;
cout << "You moved Right";
break;
};
//system("cls"); //Erase all
//draw(Player.x, Player.y); //Redraw everything, with Player.x and Player.y supposedly modified
};
};
};
return 0;
};
Initial Post: I'm trying to do something like an Undertale normal fight and now I'm doing the "dodging attacks" part, but I'm stuck at making the player move (Yep, that "a") because it didn't update when I press an arrow key (for movement). It is supposed to draw, and put the Player in Player.x and Player.y, so I did something in main() to edit these variables depending on the arrow key you pressed, and then erase and re-draw with the Player.x or Player.y modified.
Here's the code:
#include <iostream>
#include <cmath>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <Windows.h>
using namespace std;
int detectKeyPressing(){
// 0: Escape
// 1: Enter
// 2: Up
// 3: Left
// 4: Down
// 5: Right
char keyPressed = 0;
while (keyPressed != 27){
if(keyPressed == 0){
keyPressed = _getch();
}
else if(keyPressed == 13){
return 1;
}
else{
switch (keyPressed) {
//Up
case 65:
return 2;
break;
//Left
case 68:
return 3;
break;
//Down
case 66:
return 4;
break;
//Right
case 67:
return 5;
break;
//Default
default:
return -1;
break;
}
}
};
return 0;
};
int mainMenu(){
int enterPressed = 0;
cout << "Press Enter to Begin, or ESC to exit" << endl;
enterPressed = detectKeyPressing();
system("cls");
return enterPressed;
};
void draw(int playerX, int playerY) {
//Player coordinates, made for testing
cout << "Player.x = " << playerX << endl << "Player.y = " << playerY << endl;
//The next 8 spaces go blank
for (int i = 1; i < 8; i++) {
cout << endl;
}
//Square Limit Making
//Top Limit
for (int iw = 1; iw < 80; iw++) {
cout << "-";
}
cout << endl;
//Border limits and inside the Square
for (int ih = 1; ih < 30; ih++) {
//Left border
cout << "|";
//Inside the Square
for (int iw = 1; iw < 78; iw++) {
if (iw == playerX && ih == playerY){
cout << "a"; //This is supposed to be ♥ but I don't know how to put it in the screen with a cout
}
else {
cout << " ";
}
}
//Right border
cout << "|" << endl;
}
//Bottom limit
for (int iw = 1; iw < 80; iw++) {
cout << "-";
}
}
int main(){
//Hide cursor
HANDLE hCon;
hCon = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci;
cci.dwSize = 50;
cci.bVisible = TRUE;
SetConsoleCursorInfo(hCon, &cci);
//Variable Making
int gameStarted = -1; // 1 if game is running, 0 if not
//int t = 0; //Turn Counter, not useful for now
Sleep(200); //Wait to get a new seed
srand(time(NULL)); //Seed for rand()
//Menu Loop, remember, 1 if game starts running, 0 if you exit
while (gameStarted > 1 || gameStarted < 0) {
gameStarted = mainMenu();
}
//Like Void Start() in Unity
if (gameStarted == 1) {
class Player {
public:
int life = 20;
int accuracy = 80 + (rand() % 100) / 20;
int damage = 5 + (accuracy / 10) + (rand() % 100) / 50;
bool isAlive = true;
int x = 39;
int y = 24;
int speed = 2;
};
class Enemy {
public:
int life = 100;
int satisfaction = 0;
bool isAlive = true;
bool isSatisfied = false;
int damage = 2 + (rand() % 100) / 20;
};
Player Player;
Enemy Enemy;
draw(Player.x, Player.y);
//Like Void Update() in Unity
while (gameStarted != 0) {
//Draw if proyectile is moving - not yet
//Draw if player is moving (pay attention specially to this part)
if (detectKeyPressing() == 0) {
gameStarted = 0; //if ESC is pressed, exit the loop and exits
}
//If any of the Arrow Keys are pressed
else if (detectKeyPressing() > 1 && detectKeyPressing() < 6) {
switch (detectKeyPressing()) {
//Up
case 2:
Player.y -= Player.speed;
break;
//Left
case 3:
Player.x += Player.speed;
break;
//Down
case 4:
Player.y += Player.speed;
break;
//Right
case 5:
Player.x += Player.speed;
break;
};
system("cls"); //Erase all
draw(Player.x, Player.y); //Redraw everything, with Player.x and Player.y supposedly modified
};
};
};
return 0;
};
I did a few tests and it seems that the else if in "//If any of the Arrow Keys is Pressed" part isn't running but I don't know why.
I would really appreciate any help you can provide. Sorry if anything isn't well written, I'm not a native english speaker.
Where you have this comment
//Draw if proyectile is moving - not yet add a variable to save your pressed key, something like
int pressedKey = detectKeyPressing();
Then, use that variable to check which condition is met within your if-else.
What’s happening is that you’re calling your function, thus asking/waiting for an input each time a condition is being checked.
I've just recently started to learn C++. I decided to program a little Snake game that runs in the console. It is relatively simple and doesn't look amazing, but it does it's thing as it's supposed to.The only issue I am having, is that my Snake won't turn twice in a row. In other words you can't do tight U-turns with it. It will however turn immediately after pressing the button. (Unless you just turned that is).
My code is 120 lines long so here it is:
First my includes and namespace:
#include <iostream>
#include <vector>
#include <conio.h>
#include <windows.h>
#include <random>
using namespace std;
This function draws the whole field in the console:
void drawGrid(vector<vector<char>> &g, int height, int width, int score, int time)
{
SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), { 0,0 });
for (int r = 0; r < height; r++)
{
for (int c = 0; c < width; c++) std::cout << g[c][r] << " ";
std::cout << '|' << endl;
}
std::cout << "Current score: " << score << " ";
std::cout << "\nCurrent speed: " << time << " ";
}
This function checks whether the food is under the snake:
bool foodSnake(vector<int> &f, vector<vector<int>> &t, int l)
{
for (int i = 0; i < l; i++)
if (f[0] == t[i][0] && f[1] == t[i][1]) return true;
return false;
}
And this is the big poobah:
int main(void)
{
int sleeptime = 1000; // how long the break is between each frame
bool foodExists = 0;
int width = 20; //width and height of the field
int height = 15;
mt19937_64 engine; //random distributions for food generation
uniform_int_distribution<int> heightDist(0, height - 1);
uniform_int_distribution<int> widthDist(0, width - 1);
int tailLengthstart = 4;
int tailLength = tailLengthstart;
char movementDirection = 'u'; //starts moving upwards
char input;
bool alive = 1; //keeps the program running on death = 0
vector<int> pos = { (width - 1) / 2,(height - 1) / 2 }; //starts in the middle of field
vector<int> foodPos = pos; // so that the food generates at the beginning
vector<vector<int>> tail(tailLength, pos);
vector<vector<char>> emptyGrid(width, vector<char>(height, ' '));
vector<vector<char>> grid = emptyGrid;
while (alive) //runs main program until alive == 0
{
grid = emptyGrid; // clear grid
grid[pos[0]][pos[1]] = 'Q'; //place head in grid
if (!foodExists) //generates food if it was eaten
{
while (foodSnake(foodPos, tail, tailLength) || foodPos == pos)
{ // keeps regenerating until it isn't under the snake
foodPos[0] = widthDist(engine);
foodPos[1] = heightDist(engine);
}
foodExists = 1;
}
grid[foodPos[0]][foodPos[1]] = 'X'; //place food in grid
for (int i = 0; i < tailLength; i++) grid[tail[i][0]][tail[i][1]] = 'O'; // place tail in grid
drawGrid(grid, height, width, tailLength - tailLengthstart, sleeptime); //call above function to draw the grid
input = '_';
Sleep(sleeptime);
if (_kbhit()) { //this was the best way I found to wait for input
input = _getch();
switch (input)
{ //disallows moving in opposite direction otherwise changes direction
case 'w':
if (movementDirection == 'd') break;
movementDirection = 'u';
break;
case 'a':
if (movementDirection == 'r') break;
movementDirection = 'l';
break;
case 's':
if (movementDirection == 'u') break;
movementDirection = 'd';
break;
case 'd':
if (movementDirection == 'l') break;
movementDirection = 'r';
break;
case '_':
break;
}
}
for (int i = tailLength - 1; i > 0; i--)
tail[i] = tail[i - 1];
tail[0] = pos; //move the tail along
if (movementDirection == 'u') pos[1]--;
if (movementDirection == 'l') pos[0]--;
if (movementDirection == 'r') pos[0]++;
if (movementDirection == 'd') pos[1]++; // move the head
if (pos[0] < 0 || pos[0] > width - 1 || pos[1] < 0 || pos[1] > height - 1)
alive = 0; // if head is out of bounds -> dead
for (int i = 0; i < tailLength; i++)
if (pos == tail[i])
alive = 0; // if head is on tail -> dead
if (foodPos == pos)
{ // if head is on food -> eat
foodExists = 0; // food needs to be generated
tail.push_back(tail[tailLength - 1]); //tail adds a link
tailLength++; // tail is now longer
if (tailLength % 5 == 0) sleeptime *= 0.75; // at certain lengths game speeds up
}
}
this next part happens once you are dead or alive == 0
std::system("cls");
std::cout << endl << endl << endl << endl << "\tYou have died" << endl << endl << endl << endl;
std::cout << endl;
std::system("pause");
return 0;
}
So if anyone has an idea why it's not turning quickly, please help. Or any other improvement ideas are welcome as well.
The problem with movement is caused by fact that you advance tail and changing direction causes head advance immediately. That means that there is always a step before snake would actually turn.
The state set as I see it:
Setup scene.
Check key.
Change direction of movement if key pressed and it isn't opposite of current direction.
Make old head tail and add a head element in set direction from old head.
Check for death
Check for food.
If food found, grow tail by changing TailLength.
If snake length is greater than TailLength, remove tail elements until they are equal.
Render scene
Sleep and go to 2.
It is better to represent snake by a list, not by a vector. It won't reallocate memory on size change or if you will cut first elements out.
If your platform is Windows (obviously, you're using Sleep() function instead of usleep), the answer to this question offers better solution for key detection.
Get key press in windows console
Similar solutions exist for POSIX platforms.
There is a little problem that you're waiting sleeptime even the key was pressed
Sleep(sleeptime);
The main cycle may be like this pseudo code
while (IsAlive())
{
currtime = 0;
DrawState();
while (currtime++ < sleeptime)
{
if (CheckAndProcessKeyboardInput())
break;
SleepOneMilliSecond();
}
}
I don't Understand with this case but this is really really important for me, Please Help me...
void __fastcall TForm1::Button4Click(TObject *Sender)
{
String masuk, keluar, kosong;
int i, x, j, n = 0;
masuk = Edit2->Text;
keluar = masuk;
kosong = " ";
n = 0;
x = 0;
mulai:
i = 1;
j = 0;
j = j + n;
i = i + j;
if (masuk[i] == 'a')
{
keluar[i] = 't';
}
else if (masuk[i] == 't')
{
keluar[i] = 'a';
}
else if (masuk[i] == 'c')
{
keluar[i] = 'g';
}
else if (masuk[i] == 'g')
{
keluar[i] = 'c';
}
else
{
Application->MessageBoxA("Masukan Anda Salah", "Peringatan", MB_OK | MB_ICONWARNING);
keluar = kosong;
goto end;
}
n = n + 1;
if (i < 10)
goto mulai;
else
goto end;
end:
Memo1->Text = keluar;
}
if I make masukan more than 10 (i<10 (10 as default value)), it is ok but if it less than 10, it will make message exception Class EAccessViolation..
Taking a shot in the dark, but I think what you're actually trying to do might be this. I'm assuming that you're taking a single string of 10 characters which represents one half of a genome and you're generating another string of the pair values.
void __fastcall TForm1::Button4Click(TObject *Sender)
{
String masuk, keluar;
masuk = Edit2->Text;
keluar = masuk;
char kosong = ' ';
for (int i=0; i < 10; i++)
{
switch(masuk[i]) {
case 'a':
keluar[i] = 't';
break;
case 't':
keluar[i] = 'a';
break;
case 'c':
keluar[i] = 'g';
break;
case 'g':
keluar[i] = 'c';
break;
default:
Application->MessageBoxA("Masukan Anda Salah", "Peringatan", MB_OK | MB_ICONWARNING);
keluar[i] = kosong;
break;
}
Memo1->Text = keluar;
}
Working through more book examples- this one is a partial poker program-
This segment deals with straight hand....
First what was given- only relevant parts....will provide entire code if needed...
int suits[5]; //index 1..4- value start at 1
int values[14]; //index 1..13- value same as rank, A = 1, K = 13
cin.get(rankCh);
switch (toUpper(rankCh)) {
case 'A': values = 1; break;
case '2': values = 2; break;
case '3': values = 3; break;
case '4': values = 4; break;
case '5': values = 5; break;
case '6': values = 6; break;
case '7': values = 7; break;
case '8': values = 8; break;
case '9': values = 9; break;
case 'T': values = 10; break;
case 'J': values = 11; break;
case 'Q': values = 12; break;
case 'K': values = 13; break;
default:
badCard = true;
}
Other functions:
bool isFlush(int suits[]) {
for(i = 1; i <= 4; i++)
if (suits[i] == 5) //5 here is Number of Cards
return true;
return false;
}
Yeah, I know about the array declarations but that is how it is defined- nice justification for it in the text...starting to number at 1
I want my straight hand to handle both Ace high and low- right now as define above aces are low...
Two versions: 1st appears not sure correct with low aces...
CODE
bool isStraight(int values[]) //Version one only straight- low aces only
{
int count = 0;
for (i = 1; i <= 13; i++) {
if (values[i] != 1) {
count++;
} else
count = 0;
if (count == 5) //5 is NUMCARDS
return true;
}
return false;
}
Now this is the where I need some recommendation: to have a function to handle both ace high and low:
bool isStraight(int values[]) //Version handles both high and low
{
int count = 0;
for (i = 1; i <= 13; i++) {
if (values[i] != 1) {
count++;
// if(i == 1 && values[1] != 0) //Check for high and low
// count++;
} else
count = 0;
if (count == 5) //5 is NUMCARDS
return true;
}
return false;
}
Would what I have in comments work to handle both ace high and low...
Since i = 1 is represented as ace and not sure what values[1] is correct should it be values[13] or what...maybe something like
if (i == 1)
values[13] //not sure...
Recommendations-
do not want wholesale changes- just to have minor changes with what I have...I do not want to sort or solve by brute force i.e like values[1] == 1 && values [2] ==1 you get the point- the text does that already but I am trying to rewrite it this way...
Thanks...Hope I am getting across my modification I would like...
EDIT: I figured I'd would first answer your question directly. Lets first clear up how the original algorithm worked. Basically it loops from 1 to 13, and each time it sees a card in that slot, it adds to count. If anything ever breaks the sequence, it resets the counter. Finally, if the counter reaches 5, you have a straight.
I can't say off hand if your solution would work, I say give it a go. However, a simple quick patch to the original would probably go something like this:
//Version handles both high and low
bool isStraight(int values[]) {
int count = 0;
for (i = 1; i <= 13; i++) {
if (values[i] != 1) {
count++;
} else
count = 0;
if (count == 5) //5 is NUMCARDS
return true;
}
// handle ace high.
if(count == 4 && values[1] != 0) {
return true;
}
return false;
}
Basically what that does is say "if we already have 4 in a row, and we've just looked at the very last card (the loop is over), then check an ace is there, if so, we do have a straight and it is ace high".
ORIGINAL ANSWER:
I think the easiest way to handle ace high and low is to have the "get rank" function have two modes, one which returns ace high, the other which returns ace low. Then just calculate the hand value for each case and take the better one.
Also, your get rank could be way simpler :-P.
int get_rank(char card) {
static const char *cards = "A23456789TJQK";
char *p = strchr(cards, toupper(card));
if(p) {
return (p - cards) + 1;
} else {
return -1;
}
}
so if you want to have a get_rank which has an ace_high or an ace_low, you could do this:
int get_rank(char card, bool ace_high) {
static const char *cards_high = "23456789TJQKA";
static const char *cards_low = "A23456789TJQK";
const char *cards = ace_high ? cards_high : cards_low;
char *p = strchr(cards, toupper(card));
if(p) {
return (p - cards) + 1;
} else {
return -1;
}
}
EDIT:
for fun, i've made a quick and dirty program which detects straights (handling both high and low ace). It is fairly simple, but could be shorter (also note that there is no attempt at buffer safety with these arrays, something of production quality should use something safer such as std::vector:
#include <algorithm>
#include <iostream>
#include <cstring>
int get_rank(char card, bool ace_high) {
static const char *cards_high = "23456789TJQKA";
static const char *cards_low = "A23456789TJQK";
const char *cards = ace_high ? cards_high : cards_low;
char *p = strchr(cards, toupper(card));
if(p) {
return (p - cards) + 1;
} else {
return -1;
}
}
bool is_rank_less_low(int card1, int card2) {
return get_rank(card1, false) < get_rank(card2, false);
}
bool is_rank_less_high(int card1, int card2) {
return get_rank(card1, true) < get_rank(card2, true);
}
bool is_straight(int hand[], bool ace_high) {
std::sort(hand, hand + 5, ace_high ? is_rank_less_high : is_rank_less_low);
int rank = get_rank(hand[0], ace_high);
for(int i = 1; i < 5; ++i) {
int new_rank = get_rank(hand[i], ace_high);
if(new_rank != rank + 1) {
return false;
}
rank = new_rank;
}
return true;
}
bool is_straight(int hand[]) {
return is_straight(hand, false) || is_straight(hand, true);
}
int main() {
int hand1[5] = { 'T', 'J', 'Q', 'K', 'A' };
int hand2[5] = { 'A', '2', '3', '4', '5' };
std::cout << is_straight(hand1) << std::endl;
std::cout << is_straight(hand2) << std::endl;
}
The case where an ace-high straight exists can be found by changing the final test:
if (count == 5 || count == 4 && values[1] == 1) // 2nd case handles ace-high straight
return true;
It's a special case, so it must be handled separately.