Tic tac toe Minimax Algorithm Having Weird Behavior (C++) - c++

The other day, I wrote a console game of Tic-Tac-Toe in c++ for my son. He wanted me to add a computer, and I ended us using the minimax algorithm for the first time. I did some quick testing, but really just gave my laptop to my son as soon as it was printing stuff, who played with it for a couple minuets. I looked over his sholder once or twice, and noticed that it wasn't playing optimally, iv'e been trying to debug it, but I can't see where it goes wrong. I tried getting rid of alpha beta prunning, but that did not change anything.
For context, on the board the computer is -1, blank is 0, and the player is 1.
Here is the minimax function:
int minimax(int board[9], int depth, int alpha, int beta, bool isMaxizimaizingPlayer)
{
bool found = false;
for (int i = 0; i < 9; i++)
{
if (board[i] == 0)
{
found = true;
}
}
if (!found)
{
return eval(board);
}
if (depth == 0 || eval(board) != 0)
{
return eval(board);
}
if (isMaxizimaizingPlayer)
{
int maxEval = -2;
for (int spot = 0; spot < 9; spot++)
{
if (board[spot] == 0)
{
board[spot] = 1;
int e = minimax(board, depth - 1, alpha, beta, false);
if (e > maxEval)
{
maxEval = e;
}
//if (beta < alpha)
//{
// break;
//}
board[spot] = 0;
}
}
return maxEval;
}
else {
int minEval = 2;
for (int spot = 0; spot < 9; spot++)
{
if (board[spot] == 0)
{
board[spot] = -1;
int e = minimax(board, depth - 1, alpha, beta, true);
if (e < minEval)
{
minEval = e;
}
//if (beta < alpha)
//{
// break;
//}
board[spot] = 0;
}
}
return minEval;
}
}
To be compleate, here is my eval function:
int eval(int board[9])
{
/*horizontial*/
for (int i = 0; i < 3; i++)
{
if (board[i * 3] == board[i * 3 + 1] && board[i * 3 + 2] == board[i * 3] && board[i * 3] != 0)
{
return board[i * 3];
}
}
/*vertical*/
for (int i = 0; i < 3; i++)
{
if (board[i] == board[i + 3] && board[i] == board[i + 6] && board[i] != 0)
{
return board[i];
}
}
/*Both diags*/
if (board[4] != 0) {
if (board[0] == board[4] && board[0] == board[8])
{
return board[4];
}
if (board[2] == board[4] && board[4] == board[6])
{
return board[4];
}
}
return 0;
}
And here is the inital call:
int spot = 0;
int minEval = 2;
for (int i = 0; i < 9; i++)
{
if (board[i] == 0)
{
board[i] = -1;
int score = minimax(board, 3, -2, 2, false);
if (score < minEval) {
minEval = score;
spot = i;
}
board[i] = 0;
}
}
std::cout << "The computer went in spot " << spot + 1 << std::endl;
board[spot] = -1;
printBoard(board);

It looks like you only call minimax with a depth of three, so the algorithm will only look up to three moves ahead, if you want optimal play you need to set the depth to > 9, so that the agent is always looking ahead to the end of the game.

Related

Watershed implementation in C++

I'm working in the watershed algortih in C++. I have implemented a source that i've found but i didn't get the expected results. I obtain:
[
But the result should be this:
[
I have charge the image .bmp into a matrix an then i obtain the Gradient of the image using the Sobel operator.
My wathershed algorith now is:
void Watershed() {
stack<punto> s;
punto p, neighbour;
C_Matrix prueba3 (Gradiente.FirstRow(), Gradiente.LastRow(), Gradiente.FirstCol(), Gradiente.LastCol(), -1);
int auxU, auxV, Eaux, L=1;
for (double g = Gradiente.Min(); g <= Gradiente.Max(); g++) {
for (int i = Gradiente.FirstRow(); i <= Gradiente.LastRow(); i++) {
for (int j = Gradiente.FirstCol(); j <= Gradiente.LastCol(); j++) {
if (Gradiente(i, j) == g) {
p.Guarda(i, j);
s.push(p);
}
while (s.empty() == 0) {
p = s.top();
s.pop();
auxU = p.x;
auxV = p.y;
Eaux = -1;
// 8-connectivity
for (int i = 0; i < 8; i++) {
if (i == 0)
neighbour.Guarda(auxU - 1, auxV - 1);
else if (i == 1)
neighbour.Guarda(auxU, auxV - 1);
else if (i == 2)
neighbour.Guarda(auxU + 1, auxV - 1);
else if (i == 3)
neighbour.Guarda(auxU - 1, auxV);
else if (i == 4)
neighbour.Guarda(auxU + 1, auxV);
else if (i == 5)
neighbour.Guarda(auxU - 1, auxV + 1);
else if (i == 6)
neighbour.Guarda(auxU, auxV + 1);
else
neighbour.Guarda(auxU + 1, auxV + 1);
if (neighbour.x >= Gradiente.FirstRow() && neighbour.x <= Gradiente.LastRow()
&& neighbour.y >= Gradiente.FirstCol() && neighbour.y <= Gradiente.LastCol()) {
if (prueba3(neighbour.x, neighbour.y) > 0) {
if (Eaux == -1) {
Eaux = prueba3(neighbour.x, neighbour.y);
}
else if (prueba3(neighbour.x, neighbour.y) != Eaux)
Eaux = 0;
}
}
}
if (auxU >= Gradiente.FirstRow() && auxU <= Gradiente.LastRow()
&& auxV >= Gradiente.FirstCol() && auxV <= Gradiente.LastCol()) {
if (Eaux >= 0) {
prueba3(auxU, auxV) = Eaux;
}
else {
prueba3(auxU, auxV) = L;
L++;
}
}
else {
C_Print("Se sale");
C_PrintNum("AuxU", auxU);
C_PrintNum("AuxV", auxV);
}
}
}
}
}
C_PrintNum("L = ", L);
double max = prueba3.Max();
if (max > 255.0) {
prueba3.Stretch(0, 255);
}
aux = C_Image(prueba3);
}
I don't know where is the fail, maybe my source has mistakes.

Qt creator multiple definition within build folder

I am getting a weird error message with Qt creator which makes no sense why it is occuring at all.. I am at the moment programming an ai player for a ludo simulator, which has been written as a QT gui. I've creates a C++ class named player_q_learning, but for some reason is moc_player_q_learning.cpp created within the build folder, and create a multiple definition of the function which i already defined in player_q_learning... Why am I running into this error?
/home/Vato/Desktop/ludo-gui/build-ludo-Desktop-Debug/moc_player_q_learning.cpp:116: error: multiple definition of `player_q_learning::calc_input(float*, int, int)'
ludo.pro
#-------------------------------------------------
#
# Project created by QtCreator 2016-03-15T10:40:30
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = ludo
TEMPLATE = app
SOURCES += main.cpp\
dialog.cpp \
game.cpp \
ludo_player.cpp \
ludo_player_random.cpp \
player_q_learning.cpp
HEADERS += dialog.h \
game.h \
ludo_player.h \
positions_and_dice.h \
ludo_player_random.h \
player_q_learning.h
FORMS += dialog.ui
CONFIG += object_with_source
QMAKE_CXXFLAGS += -std=c++11 -Wall -Wextra -Wshadow -Wnon-virtual-dtor -pedantic -Wunused
main.cpp
#include "dialog.h"
#include <QApplication>
#include "game.h"
#include <vector>
#include "ludo_player.h"
#include "ludo_player_random.h"
#include "positions_and_dice.h"
Q_DECLARE_METATYPE( positions_and_dice )
using namespace std;
int main(int argc, char *argv[]){
QApplication a(argc, argv);
qRegisterMetaType<positions_and_dice>();
//instanciate the players here
ludo_player p1, p2;
ludo_player_random p3, p4;
game g;
g.setGameDelay(010); //if you want to see the game, set a delay
// Add a GUI <-- remove the '/' to uncomment block
// Dialog w;
// QObject::connect(&g,SIGNAL(update_graphics(std::vector<int>)),&w,SLOT(update_graphics(std::vector<int>)));
// QObject::connect(&g,SIGNAL(set_color(int)), &w,SLOT(get_color(int)));
// QObject::connect(&g,SIGNAL(set_dice_result(int)), &w,SLOT(get_dice_result(int)));
// QObject::connect(&g,SIGNAL(declare_winner(int)), &w,SLOT(get_winner(int)));
// w.show();
//Or don't add the GUI
//QObject::connect(&g,SIGNAL(close()),&a,SLOT(quit()));
//*/
//set up for each player
QObject::connect(&g, SIGNAL(player1_start(positions_and_dice)),&p1,SLOT(start_turn(positions_and_dice)));
QObject::connect(&p1,SIGNAL(select_piece(int)), &g, SLOT(movePiece(int)));
QObject::connect(&g, SIGNAL(player1_end(std::vector<int>)), &p1,SLOT(post_game_analysis(std::vector<int>)));
QObject::connect(&p1,SIGNAL(turn_complete(bool)), &g, SLOT(turnComplete(bool)));
QObject::connect(&g, SIGNAL(player2_start(positions_and_dice)),&p2,SLOT(start_turn(positions_and_dice)));
QObject::connect(&p2,SIGNAL(select_piece(int)), &g, SLOT(movePiece(int)));
QObject::connect(&g, SIGNAL(player2_end(std::vector<int>)), &p2,SLOT(post_game_analysis(std::vector<int>)));
QObject::connect(&p2,SIGNAL(turn_complete(bool)), &g, SLOT(turnComplete(bool)));
QObject::connect(&g, SIGNAL(player3_start(positions_and_dice)),&p3,SLOT(start_turn(positions_and_dice)));
QObject::connect(&p3,SIGNAL(select_piece(int)), &g, SLOT(movePiece(int)));
QObject::connect(&g, SIGNAL(player3_end(std::vector<int>)), &p3,SLOT(post_game_analysis(std::vector<int>)));
QObject::connect(&p3,SIGNAL(turn_complete(bool)), &g, SLOT(turnComplete(bool)));
QObject::connect(&g, SIGNAL(player4_start(positions_and_dice)),&p4,SLOT(start_turn(positions_and_dice)));
QObject::connect(&p4,SIGNAL(select_piece(int)), &g, SLOT(movePiece(int)));
QObject::connect(&g, SIGNAL(player4_end(std::vector<int>)), &p4,SLOT(post_game_analysis(std::vector<int>)));
QObject::connect(&p4,SIGNAL(turn_complete(bool)), &g, SLOT(turnComplete(bool)));
g.start();
return a.exec();
}
dialog.cpp
#include "dialog.h"
#include "ui_dialog.h"
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
ui->graphicsView->setBackgroundBrush(QBrush(QColor(240,240,239)));
diceBG = scene->addRect(0,-150,100,100,QPen(Qt::black,3,Qt::SolidLine,Qt::RoundCap, Qt::RoundJoin),QBrush(Qt::green));
diceRoll = scene->addSimpleText(QString::number(0),QFont("Courier", 72, QFont::Bold, true));
diceRoll->setPos(25,-150);
// Colors
std::vector<std::pair<QColor,QColor> >base_colors {
std::make_pair(QColor(92,170,119),QColor(185,219,125)), //G
std::make_pair(QColor(237,235,89),QColor(237,234,138)), //Y
std::make_pair(QColor(92,93,170),QColor(111,111,170)), //B
std::make_pair(QColor(237,57,60),QColor(237,114,125)) //R
};
QBrush white(Qt::white);
QPen blackPen(Qt::black);
blackPen.setWidth(1);
// Cross
scene->addRect(415,-155,160,960,blackPen,QBrush(QColor(195,195,194)));
scene->addRect(15,245,960,160,blackPen,QBrush(QColor(195,195,194)));
scene->addRect(415,245,160,160,QPen(QColor(195,195,194)),QBrush(QColor(195,195,194))); //clean center
// Goal stretch
scene->addRect(50,290,350,70,blackPen,QBrush(base_colors[0].first));
scene->addRect(460,-120,70,350,blackPen,QBrush(base_colors[1].first));
scene->addRect(590,290,350,70,blackPen,QBrush(base_colors[2].first));
scene->addRect(460,420,70,350,blackPen,QBrush(base_colors[3].first));
int x_pos = -10; //start place for green
int y_pos = 220;
int offset = 70;
int small_offset = 50;
int large_offset = 80;
//home fields
home_fields.push_back(QPointF(0,0));
home_fields.push_back(QPointF(630,-170));
home_fields.push_back(QPointF(800,445));
home_fields.push_back(QPointF(190,630));
for(size_t f = 0; f < home_fields.size(); ++f){
addHomeField(home_fields[f].x(),home_fields[f].y(),QBrush(base_colors[f].first));
}
// Playing fields
std::vector<std::pair<char,char> > directions{std::make_pair(1,-1),std::make_pair(1,1),std::make_pair(-1,1),std::make_pair(-1,-1) };
for(size_t d =0; d < directions.size(); ++d){
for(int i=0; i<5;++i){
if(d % 2 == 0)
x_pos += directions[d].first * offset;
else
y_pos += directions[d].second * offset;
fieldPos.push_back(QPointF(x_pos,y_pos));
}
x_pos += directions[d].first * small_offset;
y_pos += directions[d].second * small_offset;
for(int i=0; i<5;++i){
fieldPos.push_back(QPointF(x_pos,y_pos));
if(d % 2 == 0)
y_pos += directions[d].second * offset;
else
x_pos += directions[d].first * offset;
}
for(int i=0; i<2;++i){
fieldPos.push_back(QPointF(x_pos,y_pos));
if(d % 2 == 0)
x_pos += directions[d].first * large_offset;
else
y_pos += directions[d].second * large_offset;
}
fieldPos.push_back(QPointF(x_pos,y_pos));
}
//goal stretches
for(int x=60; x<=340; x+=offset)
fieldPos.push_back(QPointF(x,300));
for(int y=-110; y<=170; y+=offset)
fieldPos.push_back(QPointF(470,y));
for(int x=880; x>=600; x-=offset)
fieldPos.push_back(QPointF(x,300));
for(int y=710; y>=430; y-=offset)
fieldPos.push_back(QPointF(470,y));
QImage globe_img("../globe.png");//http://www.clker.com/clipart-world-black-and-white.html
QImage star_img("../star.png"); //http://www.clker.com/clipart-2568.html
// QGraphicsPixmapItem globe( QPixmap::fromImage(QImage("../globe.png")));
// QGraphicsPixmapItem star( QPixmap::fromImage(QImage("../star.png")));
for(size_t c = 0; c < base_colors.size(); ++c){
scene->addEllipse(fieldPos[0+13*c].x(),fieldPos[0+13*c].y(),50,50,QPen(base_colors[c].first),QBrush(base_colors[c].second));
for(int i=1; i < 13; ++i){
if(i == 8){
QGraphicsPixmapItem * globe = new QGraphicsPixmapItem( QPixmap::fromImage(globe_img));
globe->setPos(fieldPos[i+13*c]);
globe->setScale(0.5);
scene->addItem(globe);
} else if(i == 5 || i == 11){
QGraphicsPixmapItem * star = new QGraphicsPixmapItem( QPixmap::fromImage(star_img));
star->setPos(fieldPos[i+13*c]);
star->setScale(0.5);
scene->addItem(star);
} else {
scene->addEllipse(fieldPos[i+13*c].x(),fieldPos[i+13*c].y(),50,50,blackPen,white);
}
}
}
for(size_t g = 52; g < fieldPos.size(); ++g){
scene->addEllipse(fieldPos[g].x(),fieldPos[g].y(),50,50,blackPen,white);
}
create_graphic_players();
std::vector<int> init_pos(16,-1);
update_graphics(init_pos);
}
void Dialog::update_graphics(std::vector<int> player_positions){
QPointF p;
for(size_t i = 0; i < player_positions.size(); ++i){
if(player_positions[i] == -1){
p = home_fields[i / 4];
if(i % 4 == 0)
graphic_player[i]->setPos(p.x()+65 ,p.y()+15 );
else if(i % 4 == 1)
graphic_player[i]->setPos(p.x()+65 ,p.y()+115);
else if(i % 4 == 2)
graphic_player[i]->setPos(p.x()+15 ,p.y()+65 );
else if(i % 4 == 3)
graphic_player[i]->setPos(p.x()+115,p.y()+65 );
} else if(player_positions[i] == 99){
if(i/4 == 0){
if(i % 4 == 0) graphic_player[i]->setPos(405,300); //left
else if(i % 4 == 1) graphic_player[i]->setPos(405,270);
else if(i % 4 == 2) graphic_player[i]->setPos(405,330);
else if(i % 4 == 3) graphic_player[i]->setPos(435,300);
} else if(i/4 == 1){
if(i % 4 == 0) graphic_player[i]->setPos(470,235); //up
else if(i % 4 == 1) graphic_player[i]->setPos(440,235);
else if(i % 4 == 2) graphic_player[i]->setPos(500,235);
else if(i % 4 == 3) graphic_player[i]->setPos(470,265);
} else if(i/4 == 2){
if(i % 4 == 0) graphic_player[i]->setPos(535,300); //right
else if(i % 4 == 1) graphic_player[i]->setPos(535,270);
else if(i % 4 == 2) graphic_player[i]->setPos(535,330);
else if(i % 4 == 3) graphic_player[i]->setPos(505,300);
} else if(i/4 == 3){
if(i % 4 == 0) graphic_player[i]->setPos(470,365); //down
else if(i % 4 == 1) graphic_player[i]->setPos(440,365);
else if(i % 4 == 2) graphic_player[i]->setPos(500,365);
else if(i % 4 == 3) graphic_player[i]->setPos(470,335);
}
} else {
graphic_player[i]->setPos(fieldPos[player_positions[i]]);
}
}
ui->graphicsView->repaint();
}
void Dialog::create_graphic_players(){
graphic_player.clear();
QBrush piece;
QPen blackPen(Qt::black);
blackPen.setWidth(1);
for(int c = 0; c<4; ++c){
if(c == 0){
piece = QBrush(QColor(Qt::green));
} else if(c == 1){
piece = QBrush(QColor(Qt::yellow));
} else if(c == 2){
piece = QBrush(QColor(Qt::blue));
} else if(c == 3){
piece = QBrush(QColor(Qt::red));
}
for(int i = 0; i<4; ++i){
graphic_player.push_back(scene->addEllipse(5,5,40,40,blackPen,piece));
}
}
}
Dialog::~Dialog()
{
delete ui;
}
void Dialog::showEvent(QShowEvent *) {
ui->graphicsView->fitInView(scene->itemsBoundingRect(),Qt::KeepAspectRatio);
}
void Dialog::resizeEvent(QResizeEvent *){
ui->graphicsView->fitInView(scene->itemsBoundingRect(),Qt::KeepAspectRatio);
}
void Dialog::get_winner(int color){
scene->addRect(0,500,1000,200,QPen(Qt::black,3,Qt::SolidLine,Qt::RoundCap, Qt::RoundJoin),QBrush(active_color));
QGraphicsSimpleTextItem * win = scene->addSimpleText(QString("Winner is found!"),QFont("Courier", 72, QFont::Bold, true));
win->setPos(50,550);
}
void Dialog::get_color(int color){
switch(color){
case 0:
active_color = Qt::green;
break;
case 1:
active_color = Qt::yellow;
break;
case 2:
active_color = Qt::blue;
break;
case 3:
active_color = Qt::red;
default:
break;
}
}
void Dialog::get_dice_result(int dice){
current_dice_roll = dice;
diceBG->setBrush(active_color);
diceRoll->setText(QString::number(current_dice_roll));
ui->graphicsView->repaint();
}
void Dialog::addHomeField(int x, int y,QBrush brush){
QBrush whiteBrush(Qt::white);
QPen blackPen(Qt::black);
blackPen.setWidth(1);
scene->addEllipse(x,y,180,180,blackPen,brush);
scene->addEllipse(x+65 ,y+15 ,50,50,blackPen,whiteBrush);
scene->addEllipse(x+65 ,y+115,50,50,blackPen,whiteBrush);
scene->addEllipse(x+15 ,y+65 ,50,50,blackPen,whiteBrush);
scene->addEllipse(x+115,y+65 ,50,50,blackPen,whiteBrush);
}
game.cpp
#include "game.h"
#define DEBUG 0
game::game(){
game_delay = 1000;
game_complete = false;
turn_complete = true;
for(int i = 0; i < 16; ++i){
player_positions.push_back(-1);
}
color = 3;
}
void game::reset(){
game_complete = false;
turn_complete = true;
for(auto i : player_positions){
i = -1;
}
color = 3;
}
int game::rel_to_fixed(int relative_piece_index){
return relative_piece_index + color * 4;
}
int game::isStar(int index){
if(index == 5 ||
index == 18 ||
index == 31 ||
index == 44){
return 6;
} else if(index == 11 ||
index == 24 ||
index == 37 ||
index == 50){
return 7;
}
return 0;
}
int game::isOccupied(int index){ //returns number of people of another color
int number_of_people = 0;
if(index != 99){
for(size_t i = 0; i < player_positions.size(); ++i){
if(i < color*4 || i >= color*4 + 4){ //Disregard own players
if(player_positions[i] == index){
++number_of_people;
}
}
}
}
return number_of_people;
}
bool game::isGlobe(int index){
if(index < 52){ //check only the indexes on the board, not in the home streak
if(index % 13 == 0 || (index - 8) % 13 == 0 || isOccupied(index) > 1){ //if more people of the same team stand on the same spot it counts as globe
return true;
}
}
return false;
}
void game::send_them_home(int index){
for(size_t i = 0; i < player_positions.size(); ++i){
if(i < color*4 || i >= color*4 + 4){ //this way we don't skip one player position
if(player_positions[i] == index){
player_positions[i] = -1;
}
}
}
}
void game::move_start(int fixed_piece){
if(dice_result == 6 && player_positions[fixed_piece] < 0){
player_positions[fixed_piece] = color*13; //move me to start
send_them_home(color*13); //send pieces home if they are on our start
}
}
int game::next_turn(unsigned int delay = 0){
if(game_complete){
return 0;
}
switch(color){
case 0:
case 1:
case 2:
++color;
break;
case 3:
default:
color = 0;
break;
}
global_color = color;
rollDice();
relative.dice = getDiceRoll();
relative.pos = relativePosition();
emit set_color(color);
emit set_dice_result(dice_result);
msleep(delay);
switch(color){
case 0:
emit player1_start(relative);
break;
case 1:
emit player2_start(relative);
break;
case 2:
emit player3_start(relative);
break;
case 3:
emit player4_start(relative);
default:
break;
}
return 0;
}
void game::movePiece(int relative_piece){
int fixed_piece = rel_to_fixed(relative_piece); //index of the piece in player_positions
int modifier = color * 13;
int relative_pos = player_positions[fixed_piece];
int target_pos = 0;
if(player_positions[fixed_piece] == -1){ //if the selected piece is in the safe house, try to move it to start
move_start(fixed_piece);
} else {
//convert to relative position
if(relative_pos == 99){
std::cout << "I tought this would be it ";
} else if(relative_pos < modifier) {
relative_pos = relative_pos + 52 - modifier;
} else if( relative_pos > 50) {
relative_pos = relative_pos - color * 5 - 1;
} else {//if(relative >= modifier)
relative_pos = relative_pos - modifier;
}
if(DEBUG) std::cout << "color: " << color << " pos: " << relative_pos << " + " << dice_result << " = " << relative_pos + dice_result;
//add dice roll
relative_pos += dice_result; //this is relative position of the selected token + the dice number
int jump = isStar(relative_pos); //return 0 | 6 | 7
if(jump){
if(jump + relative_pos == 57){
relative_pos = 56;
} else {
relative_pos += jump;
}
}
//special case checks
if(relative_pos > 56 && relative_pos < 72){ // go back
target_pos = 56-(relative_pos-56) + color * 5 + 1; //If the player moves over the goal, it should move backwards
}else if(relative_pos == 56 || relative_pos >= 99){
target_pos = 99;
}else if(relative_pos > 50){ // goal stretch
target_pos = relative_pos + color * 5 + 1;
} else {
int new_pos = relative_pos + color * 13;
if(new_pos < 52){
target_pos = new_pos;
} else { //wrap around
target_pos = new_pos - 52; //this is the global position wrap around at the green entry point
}
}
//check for game stuff
if(isOccupied(target_pos)){
if(isGlobe(target_pos)){
target_pos = -1; //send me home
} else {
send_them_home(target_pos);
}
}
if(DEBUG) std::cout << " => " << target_pos << std::endl;
player_positions[fixed_piece] = target_pos;
}
std::vector<int> new_relative = relativePosition();
switch(color){
case 0:
emit player1_end(new_relative);
break;
case 1:
emit player2_end(new_relative);
break;
case 2:
emit player3_end(new_relative);
break;
case 3:
emit player4_end(new_relative);
default:
break;
}
emit update_graphics(player_positions);
}
std::vector<int> game::relativePosition(){
std::vector<int> relative_positons;
int modifier = color * 13;
//from start id to end
for(int i = color*4; i < player_positions.size(); ++i){
relative_positons.push_back(player_positions[i]);
}
//from 0 to start id
for(int i = 0; i < color*4; ++i){
relative_positons.push_back(player_positions[i]);
}
for(size_t i = 0; i < relative_positons.size(); ++i){
if(relative_positons[i] == 99 || relative_positons[i] == -1){
relative_positons[i] = (relative_positons[i]);
} else if(relative_positons[i] < modifier) {
relative_positons[i] = (relative_positons[i]+52-modifier);
} else if(relative_positons[i] > 50) {
relative_positons[i] = (relative_positons[i]-color*5-1);
} else if(relative_positons[i] > modifier) {
relative_positons[i] = (relative_positons[i]-modifier);
}
}
return std::move(relative_positons);
}
void game::turnComplete(bool win){
game_complete = win;
turn_complete = true;
if(game_complete){
std::cout << "player: " << color << " won" << std::endl;
emit declare_winner(color);
}
}
void game::run() {
if(DEBUG) std::cout << "color: relative pos => fixed\n";
while(!game_complete){
if(turn_complete){
turn_complete = false;
msleep(game_delay/4);
next_turn(game_delay - game_delay/4);
}
}
emit close();
QThread::exit();
}
ludo_player.cpp
#include "ludo_player.h"
#include <random>
ludo_player::ludo_player(){
}
int ludo_player::make_decision(){
if(dice_roll == 6){
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i]<0){
return i;
}
}
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i]>=0 && pos_start_of_turn[i] != 99){
return i;
}
}
} else {
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i]>=0 && pos_start_of_turn[i] != 99){
return i;
}
}
for(int i = 0; i < 4; ++i){ //maybe they are all locked in
if(pos_start_of_turn[i]<0){
return i;
}
}
}
return -1;
}
void ludo_player::start_turn(positions_and_dice relative){
pos_start_of_turn = relative.pos;
dice_roll = relative.dice;
int decision = make_decision();
emit select_piece(decision);
}
void ludo_player::post_game_analysis(std::vector<int> relative_pos){
pos_end_of_turn = relative_pos;
bool game_complete = true;
for(int i = 0; i < 4; ++i){
if(pos_end_of_turn[i] < 99){
game_complete = false;
}
}
emit turn_complete(game_complete);
}
ludo_player_random.cpp
#include "ludo_player_random.h"
ludo_player_random::ludo_player_random(){
}
int ludo_player_random::make_decision(){
std::vector<int> valid_moves;
if(dice_roll == 6){
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i]<0){
valid_moves.push_back(i);
}
}
}
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i]>=0 && pos_start_of_turn[i] != 99){
valid_moves.push_back(i);
}
}
if(valid_moves.size()==0){
for(int i = 0; i < 4; ++i){
if(pos_start_of_turn[i] != 99){
valid_moves.push_back(i);
}
}
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> piece(0, valid_moves.size()-1);
int select = piece(gen);
return valid_moves[select];
}
void ludo_player_random::start_turn(positions_and_dice relative){
pos_start_of_turn = relative.pos;
dice_roll = relative.dice;
int decision = make_decision();
emit select_piece(decision);
}
void ludo_player_random::post_game_analysis(std::vector<int> relative_pos){
pos_end_of_turn = relative_pos;
bool game_complete = true;
for(int i = 0; i < 4; ++i){
if(pos_end_of_turn[i] < 99){
game_complete = false;
}
}
emit turn_complete(game_complete);
}
player_q_learning.cpp
http://pastebin.com/bjwFDLgj
player_q_learning.h
http://pastebin.com/zUrnvdsL
I can't add the .h due to body character limit upto 30000 chars. I hope the .cpp gives an idea of how the .h files are constructed..
You declared calc_input to be a signal, so you must not implement it. It's Qt that provides its implementation (in the MOC-generated file), such that when it is emitted it calls the slots that have been connected to it.
If you didn't intend it to be a signal (as it seems to me, given that you never connect nor emit it), move its declaration outside the signals: section, so that it becomes a "regular" method.

Loop through 2D array diagonally with random board size

I was wondering how I can loop through a two dimentional array if the size of the array is random, e.g 6x6 or 10x10 etc. The idea is to search for four of the same kind of characters, 'x' or 'o'. This is typically needed for a board game.
int main() {
int array_size = 5; // Size of array
int array_height = array_size;
bool turn = true; // true = player 1, false = player 2
bool there_is_a_winner = false;
char** p_connect_four = new char*[array_size];
for (int i = 0; i < array_size; i++) // Initialise the 2D array
{ // At the same time set a value "_" as blank field
p_connect_four[i] = new char[array_size];
for (int j = 0; j < array_size; j++) {
p_connect_four[i][j] = '_';
}
}
}
This is what I have so far, checking from [3][0] to [0][3]. But this requires me to add 2 more for loops to check [4][0] to [0][4] and [4][1] to [1][4] IF the size of the board was 5x5.
for (int i = 3, j = 0; i > 0 && j < array_size; i--, j++ ) {// CHECK DOWN up right from 3,0 -> 0,3
if (p_connect_four[i][j] == p_connect_four[i - 1][j + 1] && p_connect_four[i][j] != '_' ) {
check_diagonalRight++;
if (check_diagonalRight == 3) {
there_is_a_winner = true;
break;
}
}
else {
check_diagonalRight = 0;
}
}
if (there_is_a_winner) { // Break while loop of game.
break;
}
Obviously I want to check the whole board diagonally to the right regardless of the size of the board. Is there any other way than having 3 separate for loops for checking
[3][0] -> [0][3] , [4][0] -> [0][4] and [4][1]-> [1][4] ?
for (i = array_size - 1, j = array_size - 2;
i < array_size && i >= 0, j < array_size && j >= 0; j--)
{ // starts from [4][3] and loops to the left if arraysize = 5x5
// but works on any size
int k = i, l = j;
for (k, l; k < array_size && k > 0, l < array_size && l > 0; k--, l++)
{ // checks diagonally to the right
if (check_diagonalRight == 3)
{
there_is_a_winner = true;
break;
}
if (p_connect_four[k][l] == p_connect_four[k - 1][l + 1] &&
p_connect_four[k][l] != '_')
{ //check up one square and right one square
check_diagonalRight++;
}
else
{
check_diagonalRight = 0;
// if its not equal, reset counter.
}
}
if (there_is_a_winner)
{
break; // break for loop
}
}
if (there_is_a_winner)
{
break; // break while loop of game
}
This checks up and right no matter the size, implement it for the other angles as well and it will work for any board size. You could potentially check right and left diagonal at once with nested loops.
This will work perfectly fine for your program! I hope so!
int arraySize = 8;
for(int i=0, j=0; i<arraySize && j<arraySize; i++, j++)
{
if((i == 0 && j == 0) || (i == arraySize - 1 && j == arraySize - 1))
{
continue;
}
else
{
int k = i;
int l = j;
//This Loop will check from central line (principal diagonal) to up right side (like slash sign / (representing direction))
for(k, l; k>0 && l < arraySize - 1; k--, l++)
{
//Here check your condition and increment to your variable. like:
if (p_connect_four[k][l] == p_connect_four[k - 1][l + 1] && p_connect_four[k][l] != '_' )
{
check_diagonalRight++;
}
}
//You can break the loop here if check_diagonalRight != k then break
k = i;
l = j;
//This Loop will check from central line (principal diagonal) to down left side (like slash sign / (representing direction))
for(k, l; k<arraySize - 1 && l > 0; k++, l--)
{
//Here check your condition and increment to your variable. like:
if (p_connect_four[k][l] == p_connect_four[k + 1][l - 1] && p_connect_four[k][l] != '_' )
{
check_diagonalRight++;
}
}
if(check_diagonalRight == i+j+1)
{
there_is_a_winner = true;
break;
}
}
}
I suggest to surround your board with extra special cases to avoid to check the bound.
To test each direction I suggest to use an array of offset to apply.
Following may help:
#include <vector>
using board_t = std::vector<std::vector<char>>;
constexpr const std::size_t MaxAlignment = 4;
enum Case {
Empty = '_',
X = 'X',
O = 'O',
Bound = '.'
};
enum class AlignmentResult { X, O, None };
// Create a new board, valid index would be [1; size] because of surrounding.
board_t new_board(std::size_t size)
{
// Create an empty board
board_t board(size + 2, std::vector<char>(size + 2, Case::Empty));
// Add special surround.
for (std::size_t i = 0; i != size + 2; ++i) {
board[0][i] = Case::Bound;
board[size + 1][i] = Case::Bound;
board[i][0] = Case::Bound;
board[i][size + 1] = Case::Bound;
}
return board_t;
}
// Test a winner from position in given direction.
AlignmentResult test(
const board_t& board,
std::size_t x, std::size_t y,
int offset_x, int offset_y)
{
if (board[x][y] == Case::Empty) {
return AlignmentResult::None;
}
for (std::size_t i = 1; i != MaxAlignment; ++i) {
// Following condition fails when going 'out of bound' thanks to Case::Bound,
// else you have also to check size...
if (board[x][y] != board[x + i * offset_x][y + i * offset_y]) {
return AlignmentResult::None;
}
}
if (board[x][y] == Case::X) {
return AlignmentResult::X;
} else {
return AlignmentResult::O;
}
}
// Test a winner on all the board
AlignmentResult test(const board_t& board)
{
// offset for direction. Use only 4 direction because of the symmetry.
const int offsets_x[] = {1, 1, 1, 0};
const int offsets_y[] = {-1, 0, 1, 1};
const std::size_t size = board.size() - 1;
for (std::size_t x = 1; x != size; ++x) {
for (std::size_t y = 1; y != size; ++y) {
for (std::size_t dir = 0; dir != 4; ++dir) { // for each directions
auto res = test(board, x, y, offsets_x[dir], offsets_y[y]);
if (res != AlignmentResult::None) {
return res;
}
}
}
}
return AlignmentResult::None;
}

RobotC - Programming an Elevator

I'm designing and programming an elevator-like robot for a high school project. Could I possibly do anything to make this any simpler? Or better? I have attached a picture of my design that I made in AutoCAD Inventor with labels.
For those not familiar with RobotC or VEX (it is VERY similar to C and C++): the limit switches (limit1, limit2, ...) and bump switches (floor1, floor2, ...) are analog buttons and return a value of 0 if not pressed and 1 if pressed. The motor (mainMotor) rotates the gear which causes the mechanism to travel upwards on the slide. When the shaft sticking out the motor mechanism moves up and down, it presses limit switches and causes it to return a value of 1.
int callup [3];
int calldown [3];
int floorat[3];
int main ()
{
if (SensorValue[limit1] == 1)
{
floorat[0] = 1;
}
else
{
floorat[0] = 0;
}
if (SensorValue[limit2] == 1)
{
floorat[1] = 1;
}
else
{
floorat[1] = 0;
}
if (SensorValue[limit3] == 1)
{
floorat[2] = 1;
}
else
{
floorat[2] = 0;
}
if (SensorValue[floor1] == 1)
{
calldown[0] = 1;
SensorValue[LED1] = 1;
}
if (SensorValue[floor2] == 1 && floorat[2] == 1)
{
calldown[1] = 1;
SensorValue[LED2] = 1;
}
if (SensorValue[floor2] == 1 && floorat[0] == 1)
{
callup[1] = 1;
SensorValue[LED2] = 1;
}
if (SensorValue[floor3])
{
callup[2] = 1;
SensorValue[LED3] = 1;
}
motors ();
}
void motors ()
{
if (callup[2] == 1 && floorat[2] == 1)
{
int x = 1;
while (x < 3)
{
SensorValue[LED3] = 1;
wait(0.5);
SensorValue[LED3] = 0;
wait(0.5);
}
callup[2] = 0;
main ();
}
else if (callup[1] == 1 && floorat[1] == 1)
{
int x = 1;
while (x < 3)
{
SensorValue[LED2] = 1;
wait(0.5);
SensorValue[LED2] = 0;
wait(0.5);
}
callup[1] = 0;
main ();
}
else if (callup[0] == 1 && floorat[0] == 1)
{
int x = 1;
while (x < 3)
{
SensorValue[LED1] = 1;
wait(0.5);
SensorValue[LED1] = 0;
wait(0.5);
}
callup[0] = 0;
main ();
}
if (callup[2] == 1 && floorat[1] == 1 && calldown[0] == 0 || callup[2] == 1 && floorat[0] == 1 && callup[1] == 0)
{
startMotor(mainMotor, 60);
untilTouch(limit3);
stopMotor(mainMotor);
callup[2] = 0;
wait(1);
main ();
}
if (callup[1] == 1 && floorat[0] == 1)
{
startMotor(mainMotor, 60);
untilTouch(limit2);
stopMotor(mainMotor);
callup[1] = 0;
wait(1);
main();
}
if (calldown[1] == 1 && floorat[2] == 1)
{
startMotor(mainMotor, -60);
untilTouch(limit2);
stopMotor(mainMotor);
calldown[1] = 0;
wait(1);
main();
}
if (calldown[0] == 1 && floorat[2] == 1 && calldown[1] == 0 || calldown[0] == 1 && floorat[1] == 1)
{
startMotor(mainMotor, -60);
untilTouch(limit1);
stopMotor(mainMotor);
calldown[0] = 0;
wait(1);
main();
}
}
Although it shouldn't be a concern for this question, the 60 in the startMotor command is the speed of the motor, just to make it clearer.
Feel free to ask any more questions.
Let's define what are the states of an elevator at a given moment:
An elevator can go up, down, or be idle.
The elevator is at a given floor and go from one floor to the other when it trigger a switch:
Now, if we translate this into some pseudo code (which should be easily translated to RobotC) :
enum elevator_status = { idle, down, up };
int currentfloor; //1, 2, 3
switch(elevator_status)
{
case idle:
//we check if a button is pressed and possibly go up or down
if(SensorValue(floor1))
{
if(currentfloor > 1)
elevator_status = down;
}
else if(SensorValue(floor2))
{
if(currentfloor > 2)
elevator_status = down;
else if(currentfloor < 2)
elevator_status = up;
}
else if(SensorValue(floor3))
{
if(currentfloor < 3)
elevator_status = up;
}
break;
case up:
case down:
//we check if we trigger a floor switch and stop the elevator
if(SensorValue(limit1))
{
currentfloor = 1;
elevator_status = idle;
}
else if(SensorValue(limit2))
{
currentfloor = 2;
elevator_status = idle;
}
else if(SensorValue(limit3))
{
currentfloor = 3;
elevator_status = idle;
}
break;
}
//we set the speed of the motor
if(elevator_status == up)
{
set_motorstate(cw);
)
else if(elevator_status == down)
{
set_motorstate(ccw);
}
else if(elevator_status == idle)
{
set_motorstate(idle);
}
Note : in this code the elevator will only take care of new up and down floor calls when the elevator is idle. It does not store up and down call while it is moving and go there later. I do not know if it was a requirement for you.
I'm not familiar with RobotC or VEX, however I've noticed a certain amount of replicated operations that could be made into their own functions.
The following code snippets I would make into separate functions. So in the large function called motors you have the following set of operations:
int x = 1;
while (x < 3)
{
SensorValue[LED3] = 1;
wait(0.5);
SensorValue[LED3] = 0;
wait(0.5);
}
callup[2] = 0;
main ();
This is repeated with slightly different values.
Here I'd write a function like the following:
void adjust_sensors( size_t led, size_t level )
{
int x = 1;
while (x < 3)
{
SensorValue[led] = 1;
wait(0.5);
SensorValue[led] = 0;
wait(0.5);
}
callup[level] = 0;
main ();
}
You can do the same for the following code as well:
startMotor(mainMotor, 60);
untilTouch(limit3);
stopMotor(mainMotor);
callup[2] = 0;
wait(1);
main ();
Also it seems like the while loop will never end because the value of x never changes.
You also have a typo at the top when you declare:
int callown [2];
I presume you meant:
int calldown [2];
Would be good to add some comments to your code as well for clarity.
Hope this helps.
I could be way off, because I'm just a student with questions of my own but it looks like you may have made a mistake in your array sizes. For instance, when you declared:
int floorat[2];
This made the array size 2. Then you refer to 3 element locations in this array [0, 1, 2]. Also, can't you just use a regular integer, and assign it values 1, 2, or 3?
I would recommend redefining these varaibles as:
int callup;
int calldown;
int floorat;
Then you can avoid extra lines of code and simplify the if/else clauses to:
if (SensorValue[limit1] == 1)
{
floorat = 1;
}
if (SensorValue[limit2] == 1)
{
floorat = 2;
}
if (SensorValue[limit3] == 1)
{
floorat = 3;
}

Placing random numbers in a grid

I need to place numbers within a grid such that it doesn't collide with each other. This number placement should be random and can be horizontal or vertical. The numbers basically indicate the locations of the ships. So the points for the ships should be together and need to be random and should not collide.
I have tried it:
int main()
{
srand(time(NULL));
int Grid[64];
int battleShips;
bool battleShipFilled;
for(int i = 0; i < 64; i++)
Grid[i]=0;
for(int i = 1; i <= 5; i++)
{
battleShips = 1;
while(battleShips != 5)
{
int horizontal = rand()%2;
if(horizontal == 0)
{
battleShipFilled = false;
while(!battleShipFilled)
{
int row = rand()%8;
int column = rand()%8;
while(Grid[(row)*8+(column)] == 1)
{
row = rand()%8;
column = rand()%8;
}
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != j)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row+k)*8+(column)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
for(int k = -j/2; k <= j/2; k++)
Grid[(row+k)*8+(column)] = 1;
battleShipFilled = true;
}
battleShips++;
}
else
{
battleShipFilled = false;
while(!battleShipFilled)
{
int row = rand()%8;
int column = rand()%8;
while(Grid[(row)*8+(column)] == 1)
{
row = rand()%8;
column = rand()%8;
}
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != i)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row)*8+(column+k)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
for(int k = -j/2; k <= j/2; k++)
Grid[(row)*8+(column+k)] = 1;
battleShipFilled = true;
}
battleShips++;
}
}
}
}
But the code i have written is not able to generate the numbers randomly in the 8x8 grid.
Need some guidance on how to solve this. If there is any better way of doing it, please tell me...
How it should look:
What My code is doing:
Basically, I am placing 5 ships, each of different size on a grid. For each, I check whether I want to place it horizontally or vertically randomly. After that, I check whether the surrounding is filled up or not. If not, I place them there. Or I repeat the process.
Important Point: I need to use just while, for loops..
You are much better of using recursion for that problem. This will give your algorithm unwind possibility. What I mean is that you can deploy each ship and place next part at random end of the ship, then check the new placed ship part has adjacent tiles empty and progress to the next one. if it happens that its touches another ship it will due to recursive nature it will remove the placed tile and try on the other end. If the position of the ship is not valid it should place the ship in different place and start over.
I have used this solution in a word search game, where the board had to be populated with words to look for. Worked perfect.
This is a code from my word search game:
bool generate ( std::string word, BuzzLevel &level, CCPoint position, std::vector<CCPoint> &placed, CCSize lSize )
{
std::string cPiece;
if ( word.size() == 0 ) return true;
if ( !level.inBounds ( position ) ) return false;
cPiece += level.getPiece(position)->getLetter();
int l = cPiece.size();
if ( (cPiece != " ") && (word[0] != cPiece[0]) ) return false;
if ( pointInVec (position, placed) ) return false;
if ( position.x >= lSize.width || position.y >= lSize.height || position.x < 0 || position.y < 0 ) return false;
placed.push_back(position);
bool used[6];
for ( int t = 0; t < 6; t++ ) used[t] = false;
int adj;
while ( (adj = HexCoord::getRandomAdjacentUnique(used)) != -1 )
{
CCPoint nextPosition = HexCoord::getAdjacentGridPositionInDirection((eDirection) adj, position);
if ( generate ( word.substr(1, word.size()), level, nextPosition, placed, lSize ) ) return true;
}
placed.pop_back();
return false;
}
CCPoint getRandPoint ( CCSize size )
{
return CCPoint ( rand() % (int)size.width, rand() % (int)size.height);
}
void generateWholeLevel ( BuzzLevel &level,
blockInfo* info,
const CCSize &levelSize,
vector<CCLabelBMFont*> wordList
)
{
for ( vector<CCLabelBMFont*>::iterator iter = wordList.begin();
iter != wordList.end(); iter++ )
{
std::string cWord = (*iter)->getString();
// CCLog("Curront word %s", cWord.c_str() );
vector<CCPoint> wordPositions;
int iterations = 0;
while ( true )
{
iterations++;
//CCLog("iteration %i", iterations );
CCPoint cPoint = getRandPoint(levelSize);
if ( generate (cWord, level, cPoint, wordPositions, levelSize ) )
{
//Place pieces here
for ( int t = 0; t < cWord.size(); t++ )
{
level.getPiece(wordPositions[t])->addLetter(cWord[t]);
}
break;
}
if ( iterations > 1500 )
{
level.clear();
generateWholeLevel(level, info, levelSize, wordList);
return;
}
}
}
}
I might add that shaped used in the game was a honeycomb. Letter could wind in any direction, so the code above is way more complex then what you are looking for I guess, but will provide a starting point.
I will provide something more suitable when I get back home as I don't have enough time now.
I can see a potential infinite loop in your code
int j = 0;
if(i == 1) j= (i+1);
else j= i;
for(int k = -j/2; k <= j/2; k++)
{
int numberOfCorrectLocation = 0;
while(numberOfCorrectLocation != i)
{
if(row+k> 0 && row+k<8)
{
if(Grid[(row)*8+(column+k)] == 1) break;
numberOfCorrectLocation++;
}
}
if(numberOfCorrectLocation !=i) break;
}
Here, nothing prevents row from being 0, as it was assignd rand%8 earlier, and k can be assigned a negative value (since j can be positive). Once that happens nothing will end the while loop.
Also, I would recommend re-approaching this problem in a more object oriented way (or at the very least breaking up the code in main() into multiple, shorter functions). Personally I found the code a little difficult to follow.
A very quick and probably buggy example of how you could really clean your solution up and make it more flexible by using some OOP:
enum Orientation {
Horizontal,
Vertical
};
struct Ship {
Ship(unsigned l = 1, bool o = Horizontal) : length(l), orientation(o) {}
unsigned char length;
bool orientation;
};
class Grid {
public:
Grid(const unsigned w = 8, const unsigned h = 8) : _w(w), _h(h) {
grid.resize(w * h);
foreach (Ship * sp, grid) {
sp = nullptr;
}
}
bool addShip(Ship * s, unsigned x, unsigned y) {
if ((x <= _w) && (y <= _h)) { // if in valid range
if (s->orientation == Horizontal) {
if ((x + s->length) <= _w) { // if not too big
int p = 0; //check if occupied
for (int c1 = 0; c1 < s->length; ++c1) if (grid[y * _w + x + p++]) return false;
p = 0; // occupy if not
for (int c1 = 0; c1 < s->length; ++c1) grid[y * _w + x + p++] = s;
return true;
} else return false;
} else {
if ((y + s->length) <= _h) {
int p = 0; // check
for (int c1 = 0; c1 < s->length; ++c1) {
if (grid[y * _w + x + p]) return false;
p += _w;
}
p = 0; // occupy
for (int c1 = 0; c1 < s->length; ++c1) {
grid[y * _w + x + p] = s;
p += _w;
}
return true;
} else return false;
}
} else return false;
}
void drawGrid() {
for (int y = 0; y < _h; ++y) {
for (int x = 0; x < _w; ++x) {
if (grid.at(y * w + x)) cout << "|S";
else cout << "|_";
}
cout << "|" << endl;
}
cout << endl;
}
void hitXY(unsigned x, unsigned y) {
if ((x <= _w) && (y <= _h)) {
if (grid[y * _w + x]) cout << "You sunk my battleship" << endl;
else cout << "Nothing..." << endl;
}
}
private:
QVector<Ship *> grid;
unsigned _w, _h;
};
The basic idea is create a grid of arbitrary size and give it the ability to "load" ships of arbitrary length at arbitrary coordinates. You need to check if the size is not too much and if the tiles aren't already occupied, that's pretty much it, the other thing is orientation - if horizontal then increment is +1, if vertical increment is + width.
This gives flexibility to use the methods to quickly populate the grid with random data:
int main() {
Grid g(20, 20);
g.drawGrid();
unsigned shipCount = 20;
while (shipCount) {
Ship * s = new Ship(qrand() % 8 + 2, qrand() %2);
if (g.addShip(s, qrand() % 20, qrand() % 20)) --shipCount;
else delete s;
}
cout << endl;
g.drawGrid();
for (int i = 0; i < 20; ++i) g.hitXY(qrand() % 20, qrand() % 20);
}
Naturally, you can extend it further, make hit ships sink and disappear from the grid, make it possible to move ships around and flip their orientation. You can even use diagonal orientation. A lot of flexibility and potential to harness by refining an OOP based solution.
Obviously, you will put some limits in production code, as currently you can create grids of 0x0 and ships of length 0. It's just a quick example anyway. I am using Qt and therefore Qt containers, but its just the same with std containers.
I tried to rewrite your program in Java, it works as required. Feel free to ask anything that is not clearly coded. I didn't rechecked it so it may have errors of its own. It can be further optimized and cleaned but as it is past midnight around here, I would rather not do that at the moment :)
public static void main(String[] args) {
Random generator = new Random();
int Grid[][] = new int[8][8];
for (int battleShips = 0; battleShips < 5; battleShips++) {
boolean isHorizontal = generator.nextInt(2) == 0 ? true : false;
boolean battleShipFilled = false;
while (!battleShipFilled) {
// Select a random row and column for trial
int row = generator.nextInt(8);
int column = generator.nextInt(8);
while (Grid[row][column] == 1) {
row = generator.nextInt(8);
column = generator.nextInt(8);
}
int lengthOfBattleship = 0;
if (battleShips == 0) // Smallest ship should be of length 2
lengthOfBattleship = (battleShips + 2);
else // Other 4 ships has the length of 2, 3, 4 & 5
lengthOfBattleship = battleShips + 1;
int numberOfCorrectLocation = 0;
for (int k = 0; k < lengthOfBattleship; k++) {
if (isHorizontal && row + k > 0 && row + k < 8) {
if (Grid[row + k][column] == 1)
break;
} else if (!isHorizontal && column + k > 0 && column + k < 8) {
if (Grid[row][column + k] == 1)
break;
} else {
break;
}
numberOfCorrectLocation++;
}
if (numberOfCorrectLocation == lengthOfBattleship) {
for (int k = 0; k < lengthOfBattleship; k++) {
if (isHorizontal)
Grid[row + k][column] = 1;
else
Grid[row][column + k] = 1;
}
battleShipFilled = true;
}
}
}
}
Some important points.
As #Kindread said in an another answer, the code has an infinite loop condition which must be eliminated.
This algorithm will use too much resources to find a solution, it should be optimized.
Code duplications should be avoided as it will result in more maintenance cost (which might not be a problem for this specific case), and possible bugs.
Hope this answer helps...