Related
I am given a square matrix size n and characters that the matrix contains.
For example:
3
one
two
row
I have to rotate the matrix by 45 degrees.
| | |o| | |
|o|n|e| | |t| |n| |
|t|w|o| -> |r| |w| |e|
|r|o|w| | |o| |o| |
| | |w| | |
I get
| | |o| | |
|o|n|e| | |t| |n| |
|t|w|o| -> |r| |w| |e|
|r|o|w| | |o|w|o| |
| | | | | |
This is due to rounding values.
I wrote the code. My solution is very limited, I represent it only to show the Idea and my mistakes.
The main thing I don't understand is how to store characters in array so that they have right place (coordinates x, y) after rounding floating point values to integer values. Is there a proper way of doing it?
Are there any advices or observations?
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <cmath>
struct Coordinates
{
int x, y;
};
int main()
{
int n;
std::ifstream fin("U3.txt");
fin >> n;
fin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::vector<std::vector<char>> matrix;
for (int i = 0; i < n; i++)
{
std::string temp_s;
std::vector<char> temp_v(n);
std::getline(fin, temp_s, '\n');
for (int j = 0; j < n; j++)
{
temp_v[j] = temp_s[j];
}
matrix.push_back(temp_v);
}
fin.close();
std::vector<Coordinates> cord(n * n); // Store coordinates after rotation
int index = 0;
for (int y = 0; y < n; y++)
{
for (int x = 0; x < n; x++)
{
// Multiplying two matrices
/*
[ cos(45) -sin(45) ] [ x ]
[ sin(45) con(45) ] [ y ]
=
[ sqrt(2)/2 -sqrt(2)/2 ] [ x ]
[ sqrt(2)/2 sqrt(2)/2 ] [ y ]
=
[ sqrt(2)/2 * (x - y) ]
[ sqrt(2)/2 * (x + y) ]
*/
double new_x = (std::sqrt(2) / 2 * (x - y));
double new_y = (std::sqrt(2) / 2 * (x + y));
// Trying to round value to int because the index of array is int
cord[index].x = (new_x >= 0.0 ? std::ceil(new_x) : std::floor(new_x));
cord[index].y = (new_y >= 0.0 ? std::ceil(new_y) : std::floor(new_y));
index++;
}
}
/*
Finding the min_x and min_y to know how much should I add to
the new x and y coordinates to keep the coordinates positive,
as I have to store them in array.
*/
int min_x = std::numeric_limits<int>::max();
int min_y = std::numeric_limits<int>::max();
for (int i = 0; i < n * n; i++)
{
if (min_x > cord[i].x) { min_x = cord[i].x; }
if (min_y > cord[i].y) { min_y = cord[i].y; }
}
// If there are no negative coordinates then there is nothing to add
// So I initialize min_x and min_y to 0
if (min_x >= 0) { min_x = 0; }
if (min_y >= 0) { min_y = 0; }
std::vector<std::vector<char>> new_matrix(10, std::vector<char>(10, ' '));
int row = 0, column = 0;
for (int i = 0; i < cord.size(); i++)
{
new_matrix[cord[i].y + min_y * (-1)][cord[i].x + min_x * (-1)] = matrix[row][column];
if ((i + 1) % n == 0) { row++; column = 0; continue; }
column++;
}
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
std::cout << new_matrix[i][j];
}
std::cout << std::endl;
}
return 0;
}
Hm, in my understanding, your algorithm using a rotation matrix will not work. You will always have problems with rounding. And with the correct placement of the values at the correct position in the destination matrix.
And it is even not needed, because the indices for the 2 dimensional can be calculated really simply, without any float operation.
To find a better approach, let us first analyse the possible data:
As you can see. The "next value" of an original row, has to be placed always one to the right and one down. So, for n=3. Print 1, goto right, go down, print 2, goto right, go down. And so on.
If a new source row starts, then the destination start row and start column, are the above value, but then one left and one down. And then again the same as above. Right, down, right, down. And so on and so on.
So, very simple.
Then, the size of the new matrix is always 2*n-1, because we will always have a space between 2 characters.
The destination start row is of course also 0 and the destination start column is simply n-1.
All indices are of course 0 based.
With that approach, no shifting is necessary. Target values can be placed at the correct coordinates immediately.
The solution is then straightforward:
#include <string>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <vector>
#include <iterator>
const std::string sourceFileName{ "U3.txt" };
int main() {
// Open the source File and check, if it could be opened
if (std::ifstream sourceFileStream{ sourceFileName }; sourceFileStream) {
// Read the size of the matrix (Must be > 0). Eat trailing white spaces
if (size_t matrixSize{}; (sourceFileStream >> matrixSize >> std::ws) and (matrixSize > 0)) {
// Read the complete source file
std::vector sourceRow(std::istream_iterator<std::string>(sourceFileStream), {});
// Define a 2 dim vector for target. Size is 2*n-1
std::vector<std::vector<char>> destination(matrixSize*2-1, std::vector<char>(matrixSize*2-1, ' '));
// Set start indices for destination. Please note: XStart, or Column start is always matrixSize-1
size_t startOffsetIndexDestinationColumn{ matrixSize - 1 };
size_t startOffsetIndexDestinationRow{};
// Iterate over all source rows
for (size_t row{}; (row < matrixSize) and (row < sourceRow.size()); ++row) {
// Calculate offset for coordinates in destination table
size_t offsetRow{ startOffsetIndexDestinationRow };
size_t offsetColumn{ startOffsetIndexDestinationColumn - row};
// Iterate over source columns in rows and assign value to calculated destination coordinates
for (size_t column{}; (column < matrixSize) and (column < sourceRow[row].size()); ++column) {
// Assign value
destination[row + offsetRow++][column + offsetColumn] = sourceRow[row][column];
}
}
// Show result to user. For each row in the destination vector
for (const auto& row : destination) {
// And for each column in this row
for (const char c : row) std::cout << c;
// Next row
std::cout << '\n';
}
}
else std::cerr << "\nError: Wrong dimension in source file ' " << sourceFileName << "'\n";
}
else std::cerr << "\nError: Could not open source file '" << sourceFileName << "'\n";
return 0;
}
So, selecting the proper algorithm will save you a lot of headaches.
I'm currently being bamboozled by a multidimensional array.
I know a good deal about programming and I was wondering about the logic pertaining to a question from one of quiz modules from cpp institute.
int* t[2] = { new int[2], new int[2] };
for (int i = 0; i < 4; i++)
t[i % 2][i / 2] = i;
std::cout << t[0][1] + t[1][0] << endl;
delete[] t[0];
delete[] t[1];
When the compiler gets to:
for (int i = 0; i < 4; i++)
t[i % 2][i / 2] = i;
I get a little lost, is t[int][int] manipulated every 'i' loop? I know what the answer is so I'm just looking for logic clarification to understand how multidimensional arrays are calculated in loops.
It's a way of iterating through all the combinations that works because it's only a 2x2 array.
It basically has the same effect as
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 2; j++) {
t[i][j] = i + 2*j
}
}
But instead of nesting, it's relying on the fact that you can generate the pairs 0, 0, 0, 1, 1, 0, and 1, 1 by performing arithmetic operations on 0, 1, 2, and 3:
i | i % 2 | i / 2
-----------------
0 | 0 | 0
1 | 1 | 0
2 | 0 | 1
3 | 1 | 1
Yes, you can add a print statement if you like to see it for yourself:
for (int i = 0; i < 4; i++)
{
std::cout << i % 2 << ", " << i /2 << "\n";
t[i % 2][i / 2] = i;
}
which prints:
0, 0
1, 0
0, 1
1, 1
That means that it goes through every single cell of your 2D array, in a zig-zag fashion, starting from the top left, finishing at the bottom left.
I'm having two pointers called Character *current = nullptr; and Character *target = nullptr;.
I'm changing them by a function:
void Position::Current(const Vector2f & mouse_position, Character *& current)
{
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 5; j++)
{
if ((mouse_position.x >= 245 + i * 164 && mouse_position.x <= 370 + i * 164) &&
(mouse_position.y >= 56 + j * 201 && mouse_position.y <= 221 + j * 201))
{
current = &positioning[i][j];
cout << "Selected: " << current->name << endl;
cout << endl;
current->health -= 5; //it doesn't change anything
break;
}
}
}
}
Target() function is implemented by the same way.
Both functions select the right character, but it looks like those are copies of original objects (I cant change character's statistics) .
Character positioning[4][5] is array filled with characters. I can add them on specific positions by function:
void Position::Add(Character & character, int x, int y)
{
if ((x >= 0 && x < 4) && (y >= 0 && y < 5))
{
positioning[x][y] = character; //is this a problem ?
Character::position[x][y] = true;
character.x = x;
character.y = y;
}
}
Position class has only one object.
In main() it looks like this:
positionx.Add(knight, 3, 4);
positionx.Add(demon, 2, 4);
and then:
if ((event.type == Event::MouseButtonPressed) && (event.mouseButton.button == Mouse::Right))
{
Vector2i pos = Mouse::getPosition(window);
Vector2f position = (Vector2f)pos;
positionx.Current(position, current);
}
Current function should take 5 health from selected character, but nothing like that happens.
Where have I made a mistake?
Thanks for any answer.
Problem solved!
I just had to make Character positioning[4][5] -> Character *positioning[4][5]
Then after clicking on a certain character, it finally started to behave like an original object.
I've been trying to do this program for a while now but i have a problem for some reason.
I am supposed to make a program where I have a 8*8 2D array with a mouse situated in the center of an island surrounded with by 1*1 2D array of water and a cat that is located in another fixed location (away from the mouse) The mouse is allowed to move one tile per move and can not move diagonally.
The movement is determined by a random number generator that is limited to 0,1,2 and 3 {each number represents a direction). The mouse is allowed 100 moves and then the game stops, it also stops when the mouse bumps into the cat (death), crosses a bridge that is situated in the middle of the right side(escape)or goes into water (drown) the program is supposed to run a couple of times and record the number of deaths, escapes and drowns.but I keep getting zero deaths, zero escapes and zero drowns every time i try to run the program.
here is my code:
#include<iostream>
#include<stdlib.h>
#include <time.h>
using namespace std;
#define N 9
#define M 9
int direction(int x, int A[M][N], int row, int col)
{
int m;
if (x == 0)
m = A[row - 1][col]; //North
if (x == 1)
m = A[row+1][col]; //South
if (x == 2)
m = A[row][col - 1]; //West
if (x == 3)
m = A[row][col + 1]; //East
return m;
}
int main()
{
int v, S1, S2, S3, S4;
int Mouse, Cat, Bridge;
int death = 0, escape = 0, drown = 0;
int A[M][N];
for (int a = 0;a < 10;a++)
{
for (int i = 0;i < M;i++)
S1 = A[i][0] = 4;
for (int i = 0;i < M;i++)
S2 = A[i][8] = 4;
for (int j = 0;j < N;j++)
S3 = A[0][j] = 4;
for (int j = 0;j < N;j++)
S4 = A[8][j] = 4;
for (int i = 1;i < M;i++)
{
for (int j = 1;j < N;j++)
A[i][j] = 0;
}
Mouse = A[4][4] = 1; //mouse
Cat = A[5][2] = 2; //cat
Bridge = A[4][8] = 3; //bridge
srand((unsigned)time(NULL));
for (int b = 0;b < 100;b++)
{ v = rand() % 4;
int m=direction(v, A, 4, 4);
if (m == Cat)
{
death++;
break;
}
else if (m == Bridge)
{
escape++;
break;
}
else if (m == S1 || m == S2 || m == S3 || m == S4)
{
drown++;
break;
}
}
}
cout << "The mouse died " << death << " times and escaped " << escape << " times and drowned " << drown << " times" << endl;
return 0;
}
The mouse never moves from initial location and the terrain near the mouse is safe. So nothing can happen.
Replace the cicle in the code with
int x = 4,y=4; // Mouse coords
for (int b = 0;b < 100;b++)
{ v = rand() % 4;
int m=direction(v, A, x, y);
if (m == Cat)
{
death++;
break;
}
else if (m == Bridge)
{
escape++;
break;
}
else if (m == S1 || m == S2 || m == S3 || m == S4)
{
drown++;
break;
}
// Nothing happened, move the mouse
A[x][y] = 0; // Clear the actual mouse location
if(v==0) x--;
if(v==1) x++;
if(v==2) y--;
if(v==4) y++;
A[x][y] = 1; // Mark the mouse on the map
}
There are some problems: you said the program should run a couple of times, but actually it runs just once. Worst of all, when the mouse reach the cat, the bridge, or drown, the program exits with:
return 0;
If you want to run it twice (or as many times as you want), you should put a loop like
for (int a = 0;a < 2;a++)
just after the variable declarations and before the initialization of the grid. This way, every time it runs the loop it reinitialize everything except your count variables.
Then, you should substitute the return(0) with a break;, so the inner loop exits every time something happens but the program still runs, and at the end you'll se the output print with your counts.
Oh, and I just saw you call direction with the third and fourth paarmeters fixed to "4".
...oh...and the mouse doesn't move at all. You should also update its location in the grid.
Hey all, working on a C++ little game, "Connect 3." This is just like Connect 4, except we only need a match of 3 to win the game. I am storing my board in a 2D vector, which holds ints.
vector< vector<int> > vector2d;
And I have an "X" stored as a 1, and an "O" stored as a -1, with 0 being an empty space. It seems to be working so far.
So, in my algorithm for playing against the computer, it finds the best move possible. I have the algorithm finished, but it needs to know when a "base case" has been hit. (It's recursive.) A base case is either:
Someone has gotten 3 in a row, or
The board is full
Checking if the board is full is easy. I just iterate through and see if any space is a "0". If it is, the board isn't full. But before I check that, I need to see if anyone has gotten 3 in a row, which is where I'm having issues. The only way I can think of doing this is big and complicated, going through the board 3 different times, looking for Horizontal matches of 3, vertical matches of 3, and Diagonal matches of 3. I'm not even sure where to begin in doing that, and I'm hoping there is a better way to do this. Help would be much appreciated!
Also, not sure I'm allowed to use Boost, I haven't yet so far, and I'd like to not have to use it. (Not sure if the school computers have it).
Edit: The board does not need to be 3 by 3. It could be 1 by 7, 7 by 7, or any size. If it's not a legal size (0,0), my code will tell the user that, but any other board should work. I've used the vector sizes to see how big the board is.
You don't have to check the whole board every time. Only the new piece makes a difference so you only have to check those end conditions that include the new piece. There are 8 different directions you need to check, but every two of them are on the same line and should be checked together. Directions can be defined as (delta_X, delta_Y) pairs: (1,0),(0,1),(1,1),(1,-1). Your code should traverse in each direction (as in code from Leonid) and try to count as many pieces with the same value as new piece. Then it should traverse in opposite direction which is (-x,-y) from current direction, and count those pieces as well. If the number of counted pieces is N-1 (new piece is already counted) then you have a winner.
So lets say you are using an 3x3 board. There are a finite number of winning lines that can be formed.
1 0 0 1 1 1 1 0 0 0 0 1 0 0 1 0 0 0 0 0 0 0 1 0
1 0 0 0 0 0 0 1 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1 0
1 0 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 1 1 0 1 0
Now if you give each board location a bit assignment as follows:
1 2 4
8 16 32
64 128 256
now you can work out that the 8 winning lines are as follows:
1 | 8 | 64 = 73
1 | 2 | 4 = 7
1 | 16 | 256 = 273
4 | 16 | 64 = 84
4 | 32 | 256 = 292
8 | 16 | 32 = 56
64 | 128 | 256 = 448
2 | 16 | 128 = 146
Now if you store a 1 in any bit position that a given player has you can easily step through each of the "solutions" above and test against the 8 values above.
So suppose the 2 players have the following positions:
1 1 0 0 0 1
1 0 0 0 1 1
1 0 1 0 1 0
If you tot up there values as done for the "solutions" as follows you get
1 | 2 | 8 | 64 | 256 = 331
4 | 16 | 32 | 128 = 180
So we know the winnign line is the 1 | 8 | 64 = 73 line so we can test using a bit wise and as follows
331 & 73 = 73
180 & 73 = 0
So we can easily detect that player 1 has 3 in a row and has one as the result of the "and" is not 0.
This means you can calculate a winner in a maximum of 8 steps (ie checking both players running totals against the 8 possible answers).
Obviously complexity increases as you get larger and it can seem a lot more complicated when you run out of bits (look at std::bitset, for example of how to handle that) but the end game will ALWAYS take less iterations to check than a brute force method. Obviously it takes a bit more time to set up but you only calculate the end game conditions once per board type so that time gets amortised across several plays.
Anyway ... Thats how I'd do it :D
The following C++ O(N*M) solution from algorithmical complexity perspective is the best possible as we need to check in the worst case each cell of the board. It iterates over all cells in the board (i and j), tries to go in 4 directions (k), and from there checks that 3 cells (l) in direction k are occupied and equal.
vector<vector<int> > board(n, vector<int>(m)); // initialize
/* down down-right right up-right */
int di[] = {1, 1, 0, -1 }; // four directions i coordinate
int dj[] = {0, 1, 1, 1 }; // four directions j coordinate
for (int i = 0; i < n; i++) { // for each row
for (int j = 0; j < m; j++) { // for each column
for (int k = 0; k < 4; k++) { // for each direction
int ii = i, jj = j;
bool found = true;
if (board[ii][jj] == 0) continue; // empty space
for (int l = 1; l < 3 && found; l++) { // need 3 in a row
int iii = ii + di[k], jjj = jj + dj[k];
if (iii < 0 || iii >= n) found = false, continue; // off bounds
if (jjj < 0 || jjj >= n) found = false, continue; // off bounds
if (board[iii][jjj] != board[ii][jj]) found = false;
}
if (found) {
printf("Hurray!\n");
return;
}
}
}
}
I made a game like that , the first thing I ever made in C++ actually ( Who needs hello world :P)
And everyone can use it if they want.
Just don't forget it's my first C++ thing and it's definatly not properly coded :P but it has some nice C++ things like that in it. But there's a 100% optimized search algorithm in there that checks the absolute least amount of required permutation to check three in a row win conditions with heavy commenting and ASCII art. That could be quite usefull.
Oh almost forgot the mention, It's a console application thingy (black screen DOS envi ,whatever it's called). It has an AI that (if this is my latest version) Should do pretty well. AND the grid is dynamically built (which was the hard part) U can play 3 in a row, but with a max of 20x20 grid ( lame game I found out, much more fun as 4 in a row with gravity )
Here you go:
// DrieOpEenRij.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <iostream>
#include <string>
#include <typeinfo>
using namespace std;
typedef unsigned short USHORT;
//USE ONLY IN A SQUARE GRID
//This method checks a win for the minimimum amount of spaces covering 100% amount of the grid
//It has 100% coverage and close to 0% overhead, discrimination between who to check for is required and
//so currentMove char is required to check for win on 'H' human and 'C' Computer
void CheckForWin(const char* Grid_ptr , const USHORT GridSize , const USHORT GridWidth ,bool &humanWin, bool &computerWin, const char currentMove)
{
//check for an x from 1-end of array
//for all x's check if that makes a 3 line once per linetype
//check for horizontal win (dont get overhead on edges)
//A non square grid will have been detected by now
const USHORT rowStart = 0;
const USHORT rowEnd = GridWidth-1;
USHORT passRowCounter = 1;
const USHORT Side = GridWidth;
const USHORT cond1 = rowEnd-2;
const USHORT cond2 = GridSize-Side*2;
//Check for all human win options ( after a human move )
if (currentMove == 'H')
{
//Check for human win code
//Check all array slots for an occurence of 'X'
for(USHORT i = 0; i < GridSize; i++)
{
//Local stack variables, optimizations for iterations in loops and if statements,
//also for readability, this is (only efficient and) done only when it is guaranteed
//to be used in every for jump.
USHORT iModSide = i % Side;
USHORT SideMinTwo = Side - 2;
USHORT SidePlusTwo = Side + 2;
USHORT iPlusSide = i + Side;
USHORT iPlusSideTimesTwo = i + Side * 2;
USHORT iPlusOne = i + 1;
USHORT iPlusTwo = i + 2;
//If an X is found evaluate a win scenario
if (Grid_ptr[i] == 'X')
{
//For each row -->
if (iModSide < SideMinTwo)
{
//Check horizontal win from left to right
if (Grid_ptr[i + 1] == 'X' && Grid_ptr[i + 2] == 'X')
{
humanWin = true;
break;
}
}
//For the two values under the 'X' (colomn wise) check for 'X''X'
if (iPlusSideTimesTwo < GridSize)
{
if(Grid_ptr[iPlusSide] == 'X' && Grid_ptr[iPlusSideTimesTwo] == 'X')
{
humanWin = true;
break;
}
}
//CHECK FOR DIAGONAL WIN FROM TOP LEFT TO DOWN RIGHT IN ALL POSSIBLE+LEGAL SLOTS!
// [X] [X] [?] [?] This illustration shows that checking only at X will suffice
// [X] [X] [?] [?] for this specific check in screening for all Top Left --> Down Right
// [?] [?] [?] [?] diagonal wins, similarly the Top Right --> Down Left is done mirrored
// [?] [?] [?] [?] All other wins using this vector are impossible!
// Using this amount of conditions to find it saves a lot of searching and with it time
if (iPlusSideTimesTwo < GridSize && iModSide < SideMinTwo)
{
if (Grid_ptr[i+Side+1] == 'X' && Grid_ptr[iPlusSideTimesTwo+2] == 'X')
{
humanWin = true;
break;
}
}
//CHECK FOR DIAGONAL WIN FROM TOP LEFT TO DOWN RIGHT IN ALL POSSIBLE+LEGAL SLOTS!
// [?] [?] [Y] [Y] This illustration shows that checking only at Y will suffice
// [?] [?] [Y] [Y] for this specific check in screening for all Top Right --> Down Left
// [?] [?] [?] [?] diagonal wins, similarly the Top Left --> Down Right is done mirrored
// [?] [?] [?] [?] This because all other wins using this vector are impossible!
// Using this amount of conditions to find it saves a lot of searching and with it time
if (i % Side > 1 && i + Side*2-2 < GridSize)
{
if (Grid_ptr[i+Side-1] == 'X' && Grid_ptr[i+Side*2-2] == 'X')
{
humanWin = true;
break;
}
}
} //end if arrayvalue is 'X'
} //end for each value in array
} //end if currentMove 'H'
else if (currentMove == 'C')
{
//Check for human win code
//Check all array slots for an occurence of 'X'
for(USHORT i = 0; i < GridSize; i++)
{
//Local stack variables, optimizations for iterations in loops and if statements,
//also for readability, this is (only efficient and) done only when it is guaranteed
//to be used in every for jump.
USHORT iModSide = i % Side;
USHORT SideMinTwo = Side - 2;
USHORT SidePlusTwo = Side + 2;
USHORT iPlusSide = i + Side;
USHORT iPlusSideTimesTwo = i + Side * 2;
USHORT iPlusOne = i + 1;
USHORT iPlusTwo = i + 2;
//If an X is found evaluate a win scenario
if (Grid_ptr[i] == 'O')
{
//For each row -->
if (iModSide < SideMinTwo)
{
//Check horizontal win from left to right
if (Grid_ptr[i + 1] == 'O' && Grid_ptr[i + 2] == 'O')
{
computerWin = true;
break;
}
}
//For the two values under the 'O' (colomn wise) check for 'O''O'
if (iPlusSideTimesTwo < GridSize)
{
if(Grid_ptr[iPlusSide] == 'O' && Grid_ptr[iPlusSideTimesTwo] == 'O')
{
computerWin = true;
break;
}
}
//CHECK FOR DIAGONAL WIN FROM TOP LEFT TO DOWN RIGHT IN ALL POSSIBLE+LEGAL SLOTS!
// [X] [X] [?] [?] This illustration shows that checking only at X will suffice
// [X] [X] [?] [?] for this specific check in screening for all Top Left --> Down Right
// [?] [?] [?] [?] diagonal wins, similarly the Top Right --> Down Left is done mirrored
// [?] [?] [?] [?] All other wins using this vector are impossible!
// Using this amount of conditions to find it saves a lot of searching and with it time
if (iPlusSideTimesTwo < GridSize && iModSide < SideMinTwo)
{
if (Grid_ptr[i+Side+1] == 'O' && Grid_ptr[iPlusSideTimesTwo+2] == 'O')
{
computerWin = true;
break;
}
}
//CHECK FOR DIAGONAL WIN FROM TOP LEFT TO DOWN RIGHT IN ALL POSSIBLE+LEGAL SLOTS!
// [?] [?] [Y] [Y] This illustration shows that checking only at Y will suffice
// [?] [?] [Y] [Y] for this specific check in screening for all Top Right --> Down Left
// [?] [?] [?] [?] diagonal wins, similarly the Top Left --> Down Right is done mirrored
// [?] [?] [?] [?] This because all other wins using this vector are impossible!
// Using this amount of conditions to find it saves a lot of searching and with it time
if (iPlusSideTimesTwo+2 < GridSize && iModSide < SidePlusTwo)
{
if (Grid_ptr[i+Side-1] == 'O' && Grid_ptr[i+Side*2-2] == 'O')
{
computerWin = true;
break;
}
}
} //end if arrayvalue is 'O'
} //end for each value in array
}// else if currentMove 'C'
} //end method
//useAI(char* Grid_ptr) { }
//weighGrid (char* Grid_ptr) { for (USHORT i = 0; i < GridSize(find out); i++) {} }
void PrintGrid(char* Grid_ptr, USHORT GridWidth, USHORT GridHeight, USHORT GridSize)
{
//Abort this method if the Grid is not Square
if (GridWidth != GridHeight)
{
cout << "Warning! \n\nGrid is not square. This method will likely fail!" << endl;
cout << "Aborting method!" << endl;
cout << "Press a key to return to program";
}
else
{
//Since this code block's applicable to a square grid
//Width or Height is not relevant, both should work
//I have chosen to stick with Width everywhere.
USHORT rowStart = 0;
USHORT rowEnd = GridWidth-1;
USHORT passRowCounter = 1;
USHORT Side = GridSize / GridHeight;
for(USHORT i = 0; i < Side; i++)
{
//GO TO NEXT ROW CODE
rowEnd = Side * passRowCounter;
passRowCounter++;
//PRINT ALL IN THIS ROW
for (USHORT j = rowStart; j < rowEnd; j++)
{
cout << Grid_ptr[j];
}
rowStart = rowEnd;
cout << "\n";
}
}
}
void useAI(char* Grid_ptr, USHORT GridSize, USHORT GridWidth)
{
//Check all values in the array
//If the value is '?' weigh the priority
//else continue
//Weighing the priority
//If ('O' Present in legal ranges) add prio +1
//The AI Will function on this concept
//All array slots have a weight, the highest weight means the best position
//From top prio to lowest prio that means -->
//WIN IN ONE MOVE (weight + 50)
//NOT LOSE IN ONE MOVE (weight + 15)
//BLOCK ENEMY + LINK UP OWN ( Equal prio but stacks so both matter ) weight +1
//These weights are determined using 8 directional vectors sprouting from all 'X' and 'O' locations in the grid
//In it's path if it encounters on loc 1 'X' loc 2 + weight = 50 , and vice versa, else +1 for all 8 vectors
//Create a weightgrid to store the data
USHORT* WeightGrid_ptr = new USHORT[GridSize];
USHORT* fattest_ptr = new USHORT(0);
USHORT* fattestIndex_ptr = new USHORT(0);
USHORT Side = GridWidth;
//Suggestion for optimization , make a forumula table to play all 8 vectors instead
//Per vector u need Condition for the direction first space and next space. 24 statements in a list
//A bit complex and harder to read so for now went the east 8 vectors copy pasting. But aware of the
//solution none-the-less! Unfortunatly though it seems like a maze of code, it is well documented and
//it's length is over 50% due to optimizations.
for(USHORT i = 0; i < GridSize; i++)
{
if (Grid_ptr[i] == 'X')
{
//CHECK X --> Mid Right Vector
//If within allowed parameters
if(i % Side < Side-2)
{
if(Grid_ptr[i+1] == '?' && Grid_ptr[i+2] == '?')
{
WeightGrid_ptr[i+1] += 1;
WeightGrid_ptr[i+2] += 1;
}
else if(Grid_ptr[i+1] == 'X')
{
WeightGrid_ptr[i+2] += 15;
}
else if (Grid_ptr[i+2] == 'X')
{
WeightGrid_ptr[i+1] += 15;
}
}
//CHECK X --> Down Right Vector
//If within allowed parameters
if (i % Side < Side -2 && i + Side*2 < GridSize)
{
if (Grid_ptr[i+Side+1] == '?' && Grid_ptr[i+Side*2+2] == '?')
{
WeightGrid_ptr[i+Side+1] += 1;
WeightGrid_ptr[i+Side*2+2] += 1;
}
else if(Grid_ptr[i+Side+1] == 'X')
{
WeightGrid_ptr[i+Side*2+2] += 15;
}
else if (Grid_ptr[i+Side*2+2] == 'X')
{
WeightGrid_ptr[i+Side+1] += 15;
}
}
//CHECK X --> Down Mid Vector
//If within allowed paramaters
if (i + Side*2 < GridSize)
{
if (Grid_ptr[i+Side] == '?' && Grid_ptr[i+Side*2] == '?')
{
WeightGrid_ptr[i+Side] += 1;
WeightGrid_ptr[i+Side*2] += 1;
}
else if (Grid_ptr[i+Side] == 'X')
{
WeightGrid_ptr[i+Side*2] += 15;
}
else if (Grid_ptr[i+Side*2] == 'X')
{
WeightGrid_ptr[i+Side] += 15;
}
}
//CHECK X --> Down Left Vector
//If within allowed paramaters
if(i % Side > 1 && i + Side*2 < GridSize)
{
if (Grid_ptr[i + Side*2-1] == '?' && i + Side*2-2 == '?')
{
WeightGrid_ptr[i+Side*2-1] += 1;
WeightGrid_ptr[i+Side*2-2] += 1;
}
else if(Grid_ptr[i + Side*2-2] == 'X')
{
WeightGrid_ptr[i+Side*2-1] += 15;
}
else if(Grid_ptr[i+Side*2-1] == 'X')
{
WeightGrid_ptr[i+Side*2-2] += 15;
}
}
//CHECK X --> Mid Left Vector
//If within allowed parameters
if(i % Side > 1)
{
if (Grid_ptr[i-1] == '?' && Grid_ptr[i-2] == '?')
{
WeightGrid_ptr[i-1] += 1;
WeightGrid_ptr[i-2] += 1;
}
else if(Grid_ptr[i-1] == 'X')
{
WeightGrid_ptr[i-2] += 15;
}
else if(Grid_ptr[i-2] == 'X')
{
WeightGrid_ptr[i-1] += 15;
}
}
//CHECK X --> Top Left Vector
//If within allowed parameters
if( (i) % (Side > 1) && i > Side*2)
{
if (Grid_ptr[i-Side-1] == '?' && Grid_ptr[i-Side*2-2] == '?')
{
WeightGrid_ptr[i-Side-1] += 1;
WeightGrid_ptr[i-Side*2-2] += 1;
}
else if (Grid_ptr[i-Side-1] == 'X')
{
WeightGrid_ptr[i-Side*2-2] += 15;
}
else if (Grid_ptr[i-Side*2-2] == 'X')
{
WeightGrid_ptr[i-Side-1] += 15;
}
}
//CHECK X --> Mid Top Vector
//If within allowed parameters
if (i > Side*2)
{
if(Grid_ptr[i + Side] == '?' && Grid_ptr[i + Side*2] == '?')
{
WeightGrid_ptr[i + Side] += 1;
WeightGrid_ptr[i + Side*2] += 1;
}
else if(Grid_ptr[i + Side] == 'X')
{
WeightGrid_ptr[i + Side*2] += 15;
}
else if (Grid_ptr[i + Side*2] == 'X')
{
WeightGrid_ptr[i + Side] += 15;
}
}
} //end if 'X' detected
else if (Grid_ptr[i] == 'O')
{
//CHECK 8 VECTORS
//Add weights
//CHECK O --> Mid Right Vector
//If within allowed parameters
if(i % Side < Side-2)
{
if(Grid_ptr[i+1] == '?' && Grid_ptr[i+2] == '?')
{
WeightGrid_ptr[i+1] += 1;
WeightGrid_ptr[i+2] += 1;
}
else if(Grid_ptr[i+1] == 'O')
{
WeightGrid_ptr[i+2] += 50;
}
else if (Grid_ptr[i+2] == 'O')
{
WeightGrid_ptr[i+1] += 50;
}
}
//CHECK O --> Down Right Vector
//If within allowed parameters
if (i % Side < Side -2 && i + Side*2 < GridSize)
{
if (Grid_ptr[i+Side+1] == '?' && Grid_ptr[i+Side*2+2] == '?')
{
WeightGrid_ptr[i+Side+1] += 1;
WeightGrid_ptr[i+Side*2+2] += 1;
}
else if(Grid_ptr[i+Side+1] == 'O')
{
WeightGrid_ptr[i+Side*2+2] += 50;
}
else if (Grid_ptr[i+Side*2+2] == 'O')
{
WeightGrid_ptr[i+Side+1] += 50;
}
}
//CHECK O --> Down Mid Vector
//If within allowed paramaters
if (i + Side*2 < GridSize)
{
if (Grid_ptr[i+Side] == '?' && Grid_ptr[i+Side*2] == '?')
{
WeightGrid_ptr[i+Side] += 1;
WeightGrid_ptr[i+Side*2] += 1;
}
else if (Grid_ptr[i+Side] == 'O')
{
WeightGrid_ptr[i+Side*2] += 50;
}
else if (Grid_ptr[i+Side*2] == 'O')
{
WeightGrid_ptr[i+Side] += 50;
}
}
//CHECK O --> Down Left Vector
//If within allowed paramaters
if(i % Side > 1 && i + Side*2 < GridSize)
{
if (Grid_ptr[i + Side*2-1] == '?' && i + Side*2-2 == '?')
{
WeightGrid_ptr[i+Side*2-1] += 1;
WeightGrid_ptr[i+Side*2-2] += 1;
}
else if(Grid_ptr[i + Side*2-2] == 'O')
{
WeightGrid_ptr[i+Side*2-1] += 50;
}
else if(Grid_ptr[i+Side*2-1] == 'O')
{
WeightGrid_ptr[i+Side*2-2] += 50;
}
}
//CHECK O --> Mid Left Vector
//If within allowed parameters
if(i % Side > 1)
{
if (Grid_ptr[i-1] == '?' && Grid_ptr[i-2] == '?')
{
WeightGrid_ptr[i-1] += 1;
WeightGrid_ptr[i-2] += 1;
}
else if(Grid_ptr[i-1] == 'O')
{
WeightGrid_ptr[i-2] += 50;
}
else if(Grid_ptr[i-2] == 'O')
{
WeightGrid_ptr[i-1] += 50;
}
}
//CHECK O --> Top Left Vector
//If within allowed parameters
if( (i) & (Side > 1) && i > Side*2)
{
if (Grid_ptr[i-Side-1] == '?' && Grid_ptr[i-Side*2-2] == '?')
{
WeightGrid_ptr[i-Side-1] += 1;
WeightGrid_ptr[i-Side*2-2] += 1;
}
else if (Grid_ptr[i-Side-1] == 'O')
{
WeightGrid_ptr[i-Side*2-2] += 50;
}
else if (Grid_ptr[i-Side*2-2] == 'O')
{
WeightGrid_ptr[i-Side-1] += 50;
}
}
//CHECK O --> Mid Top Vector
//If within allowed parameters
if (i > Side*2)
{
if(Grid_ptr[i + Side] == '?' && Grid_ptr[i + Side*2] == '?')
{
WeightGrid_ptr[i + Side] += 1;
WeightGrid_ptr[i + Side*2] += 1;
}
else if(Grid_ptr[i + Side] == 'O')
{
WeightGrid_ptr[i + Side*2] += 50;
}
else if (Grid_ptr[i + Side*2] == 'O')
{
WeightGrid_ptr[i + Side] += 50;
}
}
}
} // end for scan 'X' 'O'
//Get highest value from weightgrid, add an 'O' to that position, end method automatically
for (USHORT q = 0; q < GridSize; q++)
{
if (Grid_ptr[q] == '?')
{
//If a better spot is found
if (WeightGrid_ptr[q] > *fattest_ptr)
{
*fattest_ptr = WeightGrid_ptr[q];
*fattestIndex_ptr = q;
}
}
}
Grid_ptr[*fattestIndex_ptr] = 'O';
//SAFE DELETE POINTER WeightGrid_ptr
if (WeightGrid_ptr != NULL)
{
delete[] WeightGrid_ptr;
WeightGrid_ptr = NULL;
}
//SAFE DELETE POINTER fattest_ptr
if (fattest_ptr != NULL)
{
delete fattest_ptr;
fattest_ptr = NULL;
}
//SAFE DELETE POINTER fattestIndex_ptr
if (fattestIndex_ptr != NULL)
{
delete fattestIndex_ptr;
fattestIndex_ptr = NULL;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
//& adress off |-| &x = 0x?
//* value pointed by |-| a = *b
//Make the required variables on the heap
USHORT GridHeight = 0;
USHORT GridWidth = 0;
USHORT GridSize = 0;
USHORT moveCounter = 0;
char currentMove;
USHORT input;
//bool* humanWin_ptr = new bool(false);
//bool* computerWin_ptr = new bool(false);
bool humanWin_ptr = false;
bool computerWin_ptr = false;
bool Draw = false;
cout << "A challanger has arrived!" << endl;
//WARNING FOR THIS BLOCK! Special condition on for loop!
for(;;)
{
cout << "Please state the width for the grid \n";
scanf_s("%hu", &input);
if (input > 2 && input < 20)
{
GridWidth = input;
break; //CRITICAL CODE
}
else
{
cout << "Input was not correct, please state a number between 3 and 20 \n\n";
cout << "Example of correct input '3' (without quotes) \n";
}
}
//WARNING FOR THIS BLOCK! Special condition on for loop!
for(;;)
{
cout << "Please state the height for the grid \n";
scanf_s("%hu", &input);
if (input > 2 && input < 20)
{
GridHeight = input;
break; //CRITICAL CODE
}
else
{
cout << "Input was not correct, please state a number between 3 and 20 \n\n";
cout << "Example of correct input '3' (without quotes) \n";
}
}
cout << "You have succesfully filled in the paperwork to create the Grid" << endl;
GridSize = GridHeight * GridWidth;
cout << "The total GridSize is " << GridSize << " tiles in size" << endl;
//if (GridWidth != GridHeigth)
//{
// cout << "Warning! \n\nGrid is not square. Program may run irregularly!";
// cout << "Close the program or press a key to continue";
// scanf();
//}
//Note: pointer to a Grid object on the heap
char* Grid_ptr = new char[GridSize];
//Initialize Grid as empty
for (USHORT i = 0; i < GridSize; i++)
{
Grid_ptr[i] = '?';
}
//Visualize this step
cout << "Grid created as empty Grid" << endl;
cout << endl;
cout << "Please read the following introduction if you wish for an explanation of the game" << endl;
cout << "You will be reffered to as Player One equally so the opponent as AI" << endl;
cout << "You always start with the first move" << endl;
cout << "The condition for victory is a line of X X X (3 total) in a single line, colomn or a diagonal line across the Grid" << endl;
cout << "Turns are exchanged per move 1 : 1, there are no time limits so use all you need" << endl;
cout << "Player One can not lose this 3x3 Grid game when the best option is always chosen" << endl;
cout << "Consider playing a larger field if you wish to win, Best of luck!" << endl;
cout << "The grid is filled in like this!" << endl;
PrintGrid(Grid_ptr, GridWidth, GridHeight, GridSize);
while(humanWin_ptr == false && computerWin_ptr == false && Draw == false)
{
cout << "Players One's Turn! \n";
cout << "Please fill in the number your X";
currentMove = 'H';
for(;;)
{
scanf_s("%i" , &input);
if (Grid_ptr[input] == 'X' || Grid_ptr[input] == 'O')
{
cout << "That space is already taken ,try another";
}
else
{
Grid_ptr[input] = 'X';
moveCounter++;
break;
}
}
cout << '\n';
PrintGrid(Grid_ptr, GridWidth, GridHeight, GridSize);
CheckForWin(Grid_ptr, GridSize, GridWidth, humanWin_ptr, computerWin_ptr, currentMove);
cout << "AI is making a move!" << endl;
currentMove = 'C';
useAI(Grid_ptr, GridSize, GridWidth);
cout << '\n';
PrintGrid(Grid_ptr, GridWidth, GridHeight, GridSize);
CheckForWin(Grid_ptr, GridSize, GridWidth, humanWin_ptr, computerWin_ptr, currentMove);
if (humanWin_ptr)
{
cout << "Congratulations you have won the game! \n";
char c;
puts ("Enter any text. Include a Space ('.') in a sentence to exit: \n");
do
{
c=getchar();
putchar (c);
}
while (c != ' ');
}
else if (computerWin_ptr)
{
cout << "The computer won this match, better luck next time! \n";
char c;
puts ("Enter any text. Include a Space ('.') in a sentence to exit: \n");
do
{
c=getchar();
putchar (c);
}
while (c != ' ');
}
if (moveCounter >= GridSize)
{
Draw = true;
cout << "The game was a draw, good fighting!";
}
}
//int ch = 0;
//ch = _getch();
//wint_t _getwch( void );
//SAFE DELETE POINTER GRID
if (Grid_ptr != NULL)
{
delete[] Grid_ptr;
Grid_ptr = NULL;
}
/*
//SAFE DELETE POINTER Human Win
if (humanWin_ptr != NULL)
{
delete humanWin_ptr;
humanWin_ptr = NULL;
}
//SAFE DELETE POINTER Computer Win
if (computerWin_ptr != NULL)
{
delete computerWin_ptr;
computerWin_ptr = NULL;
}*/
return 0;
}
What you are asking seams to be about micro optimization. First implement it right, then profile/measure to find bottlenecks, and then think how to improve.
Since the question is so general (and without the example and code), I do not think it is possible to answer differently.