Selection of primitives when clicked by mouse not working - c

I'm developing a solitary board game, which has a piece per square, and each piece can be of two colors. If I click a piece, the four adjacent ones (top, bottom, left and right) all change to the next color.
I'm having problems with detecting in which piece the mouse was clicked on.
I have the following code for the mouse callback:
GLuint selectBuf[BUFSIZE]; // BUFSIZE is defined to be 512
GLint hits;
GLint viewport[4];
if( ( state != GLUT_DOWN ) && ( button != GLUT_LEFT_BUTTON ) )
return;
glGetIntegerv (GL_VIEWPORT, viewport);
glSelectBuffer (BUFSIZE, selectBuf);
(void) glRenderMode (GL_SELECT);
glInitNames();
glPushName(0);
gluPickMatrix ((GLdouble) x, (GLdouble) y, 20.0,20.0, viewport);
draw(GL_SELECT); // the function that does the rendering of the pieces
hits = glRenderMode(GL_RENDER);
processHits (hits, selectBuf); // a function that displays the hits obtained
Now, the problem I have is that I don't quite know how to process the hits occurred which are on selectBuf. I have the following code for processHits:
void processHits (GLint hits, GLuint buffer[])
{
unsigned int i, j;
GLuint ii, jj, names, *ptr;
printf ("hits = %d\n", hits);
ptr = (GLuint *) buffer;
for(i = 0; i < hits; i++) {
printf("hit n. %d ---> %d",i, *(buffer+i));
}
}
Finally, in the draw function I have:
void draw(GLenum mode) {
glClear (GL_COLOR_BUFFER_BIT);
GLuint x,y;
int corPeca; //colourpiece in english
int corCasa; //colourHouse (each square has a diferent color, like checkers)
for (x =0; x < colunas; x++) { //columns
for(y=0; y < colunas; y++) {
if ( (tabuleiro[y*colunas+x].peca) == 1) //board
corPeca = 1;
else
corPeca = 2;
if((tabuleiro[y*colunas+x].quadrado)==1) //square
corCasa = 1;
else
corCasa = 2;
if (mode == GL_SELECT){
GLuint name = 4;
glLoadName(name);
}
desenhaCasa(x,y,corCasa); //draws square
desenhaPeca(x,y,corPeca, mode); //draws piece
}
}
}
Now, has you can see, I've just put 4 into the buffer with glLoadName. However when I pull the number out in processHits I always get 1. I know that's because of the structure of the buffer that gets the hits, but what is that structure and how can I access the number 4?
Thank you very much for helping me.

The structure of the selection buffer is a bit more complex than that. For each hit, a "hit record" consisting of several values is appended to the selection buffer. You can look at Question 20.020 in the OpenGL FAQ for details. In your case, where there is only one name on the stack at a time, the hit record will consist 4 values, with the name being the fourth one. So, in your processHits function, you should write
for(i = 0; i < hits; i++) {
printf("hit n. %d ---> %d",i, *(buffer+4*i+3));
}
Also, the size of your name buffer should probably be 4 times longer as well.

Related

C - How to check if at least one neighbor of a cell in a matrix respects a predication?

I'm on a project where I have to code a little game named "recolor", basically it's grid of a certain size, the grid is divided in cells (we have size*size cells), I can play a color (the color have to be in the grid or in the authorized colors) and each time I'm playing a color the grid will change from the top letf corner to color each cells directly connected to the top left corner that has the same color.
That's for the global context, now I already have all the functions to play this game but I have some problems with the function I use to play a move:
The game is defined using this type :
struct game_s
{
color **grid;
color **initial_grid;
uint width;
uint height;
uint nmb_cur_mv;
uint nmb_mv_max;
};
/*
* Def :
* - neighbour_of_origin checks if a cell is in the neighborhood of the (0,0) of the game grid, to be in the neighborhood
* the cell should have the same color as the (0,0) and the cell and (0,0) have to be connected by a path of cells that have the same
* color as (0,0).
* Param :
* - cgame g : the game grid
* - int x, y : coordinates of the cell we are currently checking
* - bool** to_change_tab : 2D array of boolean that represent the game grid and have the same size as it. It is used to mark all the cells to
* modify. By default the whole grid is set to false except (0,0) which is changed no matter what.
* Ret :
* - void function, nothing to return.
*/
void neighbour_of_origin(cgame g, int x, int y, bool **to_change_tab)
{
assert(g);
assert(g->grid);
assert(to_change_tab);
color origin = g->grid[0][0];
int offset[4][2] = {
{-1, 0}, /*W*/
{0, 1}, /*S*/
{1, 0}, /*E*/
{0, -1} /*N*/
};
int off_x;
int off_y;
for (int elem = 0; elem < 4; elem++)
{
off_x = x + offset[elem][0]; /*takes the 'x' coord in the offset matrix and add it to the current x*/
off_y = y + offset[elem][1]; /*takes the 'y' coord in the offset matrix and add it to the current y*/
if (((off_x >= 0 && off_x < SIZE) && (off_y >= 0 && off_y < SIZE)) && (to_change_tab[off_x][off_y] == true) && (g->grid[x][y] == origin))
{
to_change_tab[x][y] = true;
break;
}
}
}
/*
* Def :
* - game_play_one_move will change all the cells that are neighbors of the (0,0)that have the same color as it. The changing always begin
from the (0,0) and then it expands in all directions.
* Param :
* - game g : the game grid that will be modified.
* - color c : the color that will be applied to all the cells we have to change.
* Ret :
* - void function, nothing to return.
*/
void game_play_one_move(game g, color c)
{
assert(g);
assert(g->grid);
bool **to_change_tab = (bool **)malloc(sizeof(bool *) * SIZE);
assert(to_change_tab);
for (int i = 0; i < SIZE; i++)
{
to_change_tab[i] = (bool *)malloc(sizeof(bool) * SIZE);
assert(to_change_tab[i]);
for (int j = 0; j < SIZE; j++)
{
to_change_tab[i][j] = false;
}
}
/*getting all the cells we have to change*/
to_change_tab[0][0] = true; /*(0,0) is always modified */
printf("mark tab : \n");
for (int x = 0; x < SIZE; x++)
{
for (int y = 0; y < SIZE; y++)
{
neighbour_of_origin(g, x, y, to_change_tab);
printf("%d ", to_change_tab[x][y]); /*debbuging purpose*/
}
printf("\n");
}
/*modification of the game grid */
for (int x = 0; x < SIZE; x++)
{
for (int y = 0; y < SIZE; y++)
{
if (to_change_tab[x][y] == true)
{
g->grid[x][y] = c;
}
}
}
g->nmb_cur_mv += 1;
/*free the temporary array of bools*/
for (int x = 0; x < SIZE; x++)
{
free(to_change_tab[x]);
to_change_tab[x] = NULL;
}
free(to_change_tab);
to_change_tab = NULL;
}
above we have the two functions I've made to change the color as described in the rules, It actually works pretty well but I have a big issue on it.
This is my grid at the very begining :
each number corresponds to a color, the grid is 12*12 cells.
000202101030
033111132010
101232320332
231032111220
212333320100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
Here is my grid when I play the following colors : 3 1 3 0
000202101030
000000002010
000202020332
230002111220
212300020100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
if I play any color (except 0 of course) as my next move I expect every 0 connected to the top left to change but i got this instead:
333202101030
333333332010
333232320332
233332111220
212333320100
033011233213
112220013112
131310101333
030100211130
131000323100
133112232002
202301112301
the 0 on the first line didn't changed whereas they were connected to the origin, I suspected that the bug is in my condition in neighbour_of_origin(cgame g, int x, int y, bool **to_change_tab) because i'm using a for on x and y so the first line is evaluated first and the 0 on the first line did not have any neighbor that corresponds to the if condition at this state, is there a way to evaluate in "the order of propagation",I wanted to rewrite it recursively but I'm really bad in recursion.
Very sorry for the long post, I wish someone could help me, have a good day.
edit : I got a ''''fix'''' I duplicate my for loops in the game_play_one_move() function so it will loop again and again to find each cells he couldn't find before, I know it's not a real fix because in the future i don't know how the grid will be displayed exactly, but i guess that a loop that will check again and again until it can't find any neighbor could be a solution too, in terms of colplexity it would be a real problem since i'm looping in the grid again and again and again

How to find overlap in a linearized 64x64 array

I am working with a linearized 64x64 array. There are either horizontal or vertical lines with "colors" in them set as color codes in each spot in the array, and I am trying to find the color that has no overlap.
An example of what I mean is here: https://imgur.com/a/geE73aK
It is clear that in this example, since none of the yellow are overlapped by any other colors, yellow's color code would be the solution.
It checks left/right and above/below the current position to see if the color is the same. If the color is the same, it will continue. If the color is different (AKA overlap is found), it will make that color code zero and by the end of the program all but one color code will be zero, making it easy to find the non-zero (AKA non-overlapping) color code.
I currently have this as my code. I am stuck on how to find if there is overlap and make the overlapping color code 0. Any help would be appreciated.
#include <stdio.h>
#include <stdlib.h>
#define DEBUG 1 // RESET THIS TO 0 BEFORE SUBMITTING YOUR CODE
int main(int argc, char *argv[]) {
int PileInts[1024];
int NumInts, TopColor=0;
int Load_Mem(char *, int *);
// This allows you to access the pixels (individual bytes)
// as byte array accesses (e.g., Pile[25] gives pixel 25):
char *Pile = (char *)PileInts;
if (argc != 2) {
printf("usage: ./P1-1 valuefile\n");
exit(1);
}
NumInts = Load_Mem(argv[1], PileInts);
if (NumInts != 1024) {
printf("valuefiles must contain 1024 entries\n");
exit(1);
}
if (DEBUG){
printf("Pile[0] is Pixel 0: 0x%02x\n", Pile[0]);
printf("Pile[107] is Pixel 107: 0x%02x\n", Pile[107]);
}
/* Your program goes here */
int i;
int j;
/*
int black = 0;
int pink = 1;
int red = 2;
int green = 3; // all colors and their codes
int blue = 4;
int orange = 5;
int yellow = 6;
int skyblue = 7;
*/
int rows = 63;
int cols = 63;
int colors[] = [1, 2, 3, 4, 5, 6, 7]; // color codes in array format
int current = Pile[i*64 + j]; //current position in linearized 64x64 array
for (i=1; i<63; i++){ //run through 63 rows
for (j=1; j<63; j++){ //run through 63 columns
// CHECK HORIZONTAL OVERLAP
if (current == Pile[i*64+j+1]) //check to the right current position
if (current == Pile[i*64+j-1]) //check to the left current position
// CHECK VERTICAL OVERLAP
if (current == Pile[(i-1)*64+j]) //check above current position
if (current == Pile[(i+1)*64+j]) //check below current position
}
}
//CHECK FOR COLORS WHICH DID NOT OVERLAP - THAT WILL BE SOLUTION, AS THERE SHOULD ONLY BE ONE
for (int k=0; k<8; k++){
if colors[k] != 0 {
TopColor = k;
}
}
}
As for it running from 1-63, that is okay. There will be no colors along the borders.
Clarifying information about the specifics: There will only be one line in each vertical and horizontal. each vertical or horizontal line will be at least one row/column from the other. There will be exactly one color that does not overlap.

Unable to read a maze of chars and put it into a bidimensional array

I am making a maze solving game. There is a function in the middle of the game that saves the progress and prints everything into the file. But now i want to read the maze, the character, etc from the file and put it into a bidimensional array but i am not being able to do so.
The array is declared globally (because of its use in several functions) and it is maze[30][30]. N is also declared globally as a variable size but at this point it should be 10. filePath too and it gives the name of the file.
This is how they are declared globally.
int N = 10;
char* filePath = "./save.txt";
char maze[30][30];
This is the read function:
void ler()
{
int i, j;
ex=1; ey=0;
sx=N-2; sy=N-1;
int aux;
FILE *fp = fopen(filePath, "r");
if (fp == NULL)
{
printf("Unable to perform.");
return;
}
system("cls");
for(i=0 ; i<N ; i++)
{
for(j=0 ; j<N ; j++)
{
fscanf(fp, "%c", maze[j][i]);
}
}
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
printf("%c", maze[j][i]); //Double print just for visuals
printf("%c", maze[j][i]);
}
printf("\n");
}
fclose(fp);
}
This is the save function:
void save(char maze[30][30]){
int i,j;
FILE *fp = fopen(filePath, "w");
if(fp==NULL){
printf("Unable to perform.");
return;
}
for(i=0 ; i<N ; i++){
for(j=0 ; j<N ; j++){
fprintf(fp, "%c", maze[j][i]);
fprintf(fp, "%c", maze[j][i]);
}
fprintf(fp, "\n", maze[j][i]);
}
fclose(fp);}
At this point it should only be possible to print the maze but it is not doing even that.
What save.txt file looks like after saving
The weird II is the caharcter and the other is like a highscore thing.
Using multidimensional arrays in C is actually more pain than they're worth. A much better option is to use a structure with a dynamically allocated array describing the maze, and accessor functions to examine and change the maze cells. Instead of putting markers in the maze data, you can put start/end/current location coordinates in the structure.
(I do realize that this does not answer the OP's stated question, but this is an answer to the underlying problem OP is trying to solve.)
Consider the following example. It limits the maze size to 255×255, but because each coordinate and maze cell is always just one byte, the save files are portable between architectures, as there is no byte order (endianness) to worry about. (You, as the programmer, do need to choose to use only codes 0..255 in the maze, though, to keep the data portable; the functions below won't enforce that.)
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
typedef struct {
unsigned char row;
unsigned char col;
} mazepoint;
typedef struct {
unsigned char rows;
unsigned char cols;
unsigned char *cell;
mazepoint player;
} maze;
#define OUTSIDE 0
static inline unsigned char maze_get(maze *const m,
const int row,
const int col)
{
if (m &&
row >= 0 && row < (int)(m->rows) &&
col >= 0 && col < (int)(m->cols))
return m->cell[ (size_t)col + (size_t)(m->cols) * (size_t)row ];
else
return OUTSIDE;
}
static inline unsigned char maze_set(maze *const m,
const int row,
const int col,
const unsigned char val)
{
if (m &&
row >= 0 && row < (int)(m->rows) &&
col >= 0 && col < (int)(m->cols))
return m->cell[ (size_t)col + (size_t)(m->cols) * (size_t)row ] = val;
else
return OUTSIDE;
}
static inline void maze_free(maze *const m)
{
if (m) {
free(m->cell);
m->rows = 0;
m->cols = 0;
m->cell = NULL;
}
}
int maze_create(maze *const m,
const int rows,
const int cols)
{
size_t cells = (size_t)rows * (size_t)cols;
unsigned char *cell;
if (!m)
return -1; /* NULL reference to a maze variable! */
if (rows < 1 || rows > 255 ||
cols < 1 || cols > 255)
return -1; /* Invalid number of rows or columns! */
cell = malloc(cells); /* sizeof (unsigned char) == 1. */
if (!cell)
return -1;
/* Initialize all maze cells to OUTSIDE. */
memset(cell, OUTSIDE, cells);
m->rows = rows;
m->cols = cols;
m->cell = cell;
/* Let's initialize player location to upper left corner. */
m->player.row = 0;
m->player.col = 0;
return 0; /* Success. */
}
int maze_save(maze *const m, const char *filename)
{
size_t cells;
FILE *out;
if (!m || m->rows < 1 || m->cols < 1)
return -1; /* No maze to save! */
if (!filename || !filename[0])
return -1; /* NULL or empty filename! */
cells = (size_t)(m->rows) * (size_t)(m->cols);
out = fopen(filename, "wb");
if (!out)
return -1; /* Cannot open file for writing! */
do {
/* First byte is the number of rows. */
if (fputc(m->rows, out) == EOF)
break;
/* Second byte is the number of columns. */
if (fputc(m->cols, out) == EOF)
break;
/* rows*cols bytes of maze data follows. */
if (fwrite(m->cell, 1, cells, out) != cells)
break;
/* Player location follows. */
if (fputc(m->player.row, out) == EOF)
break;
if (fputc(m->player.col, out) == EOF)
break;
/* You can save additional data at this point. */
/* That completes the save file. Ensure it is correctly saved. */
if (fflush(out))
break;
if (fclose(out))
break;
/* Maze successfully saved. */
return 0;
} while (0);
/* Save failed. */
fclose(out);
remove(filename);
return -1;
}
int maze_load(maze *const m, const char *filename)
{
size_t cells;
unsigned char *cell;
int rows, cols, r, c;
FILE *in;
if (!m)
return -1; /* No reference to a maze variable to load into! */
/* Just in case, we clear the maze first. Might help finding bugs! */
m->rows = 0;
m->cols = 0;
m->cell = NULL;
if (!filename || !filename[0])
return -1; /* NULL or empty filename! */
in = fopen(filename, "rb");
if (!in)
return -1; /* Cannot open file for reading. */
rows = fgetc(in);
cols = fgetc(in);
if (rows == EOF || rows < 1 || rows > 255 ||
cols == EOF || cols < 1 || cols > 255) {
fclose(in);
return -1; /* Not a saved maze! */
}
cells = (size_t)(rows) * (size_t)(cols);
cell = malloc(cells);
if (!cell) {
fclose(in);
return -1; /* Not enough memory available! */
}
do {
/* Read maze cell data. */
if (fread(cell, 1, cells, in) != cells)
break;
/* Player location. */
r = fgetc(in);
c = fgetc(in);
if (r == EOF || r < 0 || r > 255 ||
c == EOF || c < 0 || c > 255)
break;
m->player.row = r;
m->player.col = c;
/* Load other saved data here. */
/* All data read successfully. */
fclose(in);
m->rows = rows;
m->cols = cols;
m->cell = cell;
return 0;
} while (0);
/* Read error. */
fclose(in);
free(cell);
return -1;
}
In your own program, you'd create a maze thus:
maze m;
/* Create a 20-row, 30-column maze. */
if (maze_create(&m, 20, 30)) {
/* Failed to create maze! Show an error message. */
exit(EXIT_FAILURE);
}
To save the maze to say maze.dat, you use
m.player.row = /* row where the player is */
m.player.col = /* column where the player is */
if (maze_save(&m, "maze.dat")) {
/* Failed! Show an error message. */
exit(EXIT_FAILURE);
}
If you look at the example code, you can add additional data, especially points like the player place, to be saved and loaded along with the maze cells themselves.
To destroy a maze when it is no longer needed, use
maze_free(&m);
To load a saved maze, say from maze.dat, use
if (maze_load(&m, "maze.dat")) {
/* Failed! Show an error message. */
exit(EXIT_FAILURE);
}
/* Restore player place from m.player.row and m.player.col */
The accessor function maze_get() is not limited to the valid coordinates (0 through rows-1 or cols-1, inclusive). If you examine outside the maze itself, it will just return the value of the OUTSIDE macro. For example,
if (maze_get(&m, row, col) == 5) {
/* That cell has value 5 */
} else {
/* Either the cell has a different value,
or row,col is outside the maze. */
}
Similarly, you can try to set any cell value safely. It will only "stick" if it is within the valid maze coordinate range, however; elsewhere it will return OUTSIDE:
if (maze_set(&m, row, col, 5) == 5) {
/* Changed cell value to 5 */
} else {
/* row,col is outside the maze. */
}
The reason I wrote the accessor macros that way, is that it makes rendering only a part of the maze very simple. If the view is viewrows by viewcols in size, centered at row and col, then you can render the view using a simple loop:
const int top = row - viewrows / 2;
const int left = col - viewcols / 2;
int vr, vc;
for (vr = 0; vr < viewrows; vr++) {
for (vc = 0; vc < viewcols; vc++) {
const unsigned char v = maze_get(&m, top+vr, left+vc);
/* Draw v at row vr, col vc */
}
}
and the cells are even drawn in the same order as you read this text; from top to bottom, left to right.
Note that instead of using the maze cell values for character codes, you should use a lookup table instead. For example,
int cell_char[256];
Instead of printing cell values directly, you'd print the corresponding cell_char, for example
fputc(cell_char[maze_get(&m, row, col)], stdout);
That way you can group e.g. different wall characters into a consecutive range, or even use the individual bits in the 8-bit cell value as identifiers. The maze cells then describe the logical contents in that maze cell, rather than its visual representation, with the logical-to-visual mapping in a separate array.
If you used Gtk+, you could have an array of GtkImage pointers,
GtkImage *cell_image[256] = {0}; /* All NULL by default */
or using SDL, you could have the maze cells as textures you can render,
SDL_Texture *cell_texture[256] = {0}; /* All NULL by default */
and in both cases, read them from either one large image (say, divided into 16×16 exact same size rectangles), or from individual image files.
For example, you could decide that the four least significant bits in the cell value specify whether movement from that cell up (previous row), down (next row), left (previous column), or right (next column) is possible:
#define CAN_GO_UP(value) ((value) & (1 << 0)) /* == 1 */
#define CAN_GO_DOWN(value) ((value) & (1 << 1)) /* == 2 */
#define CAN_GO_LEFT(value) ((value) & (1 << 2)) /* == 4 */
#define CAN_GO_RIGHT(value) ((value) & (1 << 3)) /* == 8 */
Note that this allows you to do "trap walls": passages that only work one way. Maze cell values that are multiples of 16 (0, 16, 32, 48, 64, 80, 96, ..., 208, 224, and 240) represent completely blocked cells: no way out. +1 allows passage up; +2 allows passage down; +3 allows passage up and down; +4 allows passage left; +5 allows passage left and up; +6 allows passage left and down; +7 allows passage up, left, and down; +8 allows passage right; +9 allows passage up and right; +10 allows passage down and right; +11 allows passage up, down, and right; +12 allows passage left and right; +13 allows passage up, left, and right; +14 allows passage down, left, and right; and +15 allows passage up, down, left, and right.
I would personally also recommend using the wide version of the ncurses library (ncursesw). (I do not use Windows, so I am not exactly sure how you install and use it in windows, but the ncurses home page does have downloads when using mingw.)
Then, you would have a much wider variety of glyphs you could use. (When using UTF-8 locales, potentially the entire Unicode glyph set -- the Box Drawing block especially would be useful for maze drawing, and most of those glyphs are also available in the old CP437 codepage, which means they should work both in Windows and non-Windows terminals nicely.)
In that case, you'd probably use
cchar_t cell_char[256];
As I mentioned above, you could even do a graphical version (perhaps later on, extending your terminal version?) in C using SDL or GTK+. (Note that the above separation between logical maze cell content value and the visual describing the cell also means you can, at runtime, choose between "themes", by having more than one set of cell images. That allows you to start with crude informational versions, for debugging, and then add visual goodness.)
The approach shown in this answers allows you to start with a simple terminal-based game, and if you decide you want to, add support for graphical UI, with image-based maze cells, without having to rewrite any of your core maze code.

How do I interact with a grid using a 2D array (Proce55ing)

For a project I need to create Connect Four using Processing, I have created the grid which the game will be played in however I am lost when it comes to players interacting with it.
I have been told to use a 2D array however my knowledge of arrays is very limited. I am currently trying to code the bit where the program detects where a player has clicked and spawning a coin there.
int c = 8;
int r = 10;
int[][] grid = new int [c][r];
int CoinSpawn = -1;
void setup () {
size(1000, 800);
}
void draw () {
background(1, 1, 1);
translate(100, 100);
noStroke();
drawColumns();
drawRows();
}
void keyPressed () {
for (int i=0; i<grid.length-1; i++) {
grid[i][i] = grid[i+1][i+1];
}
}
void drawRows(){
for (int i=0; i < r; i++){
int x = 80;
x = x * i;
translate(x,0);
drawColumns();
translate(-x,0);
}
}
void drawColumns(){
for (int i=0; i < c; i++){
int y = 80;
y = y * i;
translate(0,y);
drawCell();
translate(0,-y);
}
}
void drawCell(){
fill(0,0,255);
rect(0,0,80,80);
noFill();
fill(0);
ellipseMode(CENTER);
translate(40,40);
ellipse(0,0,75,75);
noFill();
translate(-40,-40);
}
Would I be able to assign the 2D array to the grid? so that each slot in the grid represents an element from the array? That is the best option I can see at the moment however I have no idea how to do it.
I really appreciate any replies as I am completely lost at the moment.
You have a pretty good start, I made a lot more changes than I had planned... got a little into it!
Let me know if you have any questions, I did use one simple OOP class called cell that tracks the value and the cell's position, as well as provides a display function, I converted a lot of your variables and hard coded numbers to constants (starts with final and has the same value for the entire program)
you will notice I left the win conditions to you!
I hope this is helpful, I feel like seeing 2D arrays used in a context you are familiar with might help you understand them!
My normal process using 2D arrays for processing:
use Setup() to set initial array values
use Draw() to call each of the item's display function
use other functions to modify the array data, which the display function of the cell knows how to display
Main File
// Grid Size constants
final int GRID_COLUMNS = 10;
final int GRID_ROWS = 8;
// Display constants
final int CELL_SCALE = 80;
final int COIN_RADIUS = 75;
final int BOARD_PADDING = 100;
final color[] PLAYER_COLORS = new color[]{color(0), color(200, 10, 10), color(200, 200, 10)};
// cell values
final int EMPTY_CELL = 0;
final int PLAYER_1 = 1;
final int PLAYER_2 = 2;
// game data
Cell[][] grid = new Cell [GRID_COLUMNS][GRID_ROWS];
int currentPlayer;
void setup () {
size(1000, 800);
ellipseMode(CENTER); // only needs to be set once per sketch
setupBlankBoard();
}
// method to populate the array with blank cells, used to initiate and reset the game
void setupBlankBoard() {
currentPlayer = PLAYER_1; // reset game and start with first player
// populate array
for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row
// rather than translates I prefer to calculate the actual x,y position and just display it there,
// we add the padding offset to every cell, and then take the column/row times the width of the square cell
grid[x][y] = new Cell(BOARD_PADDING+x*CELL_SCALE, BOARD_PADDING+y*CELL_SCALE);
}
}
}
void changePlayers() {
if (currentPlayer == PLAYER_1) {
currentPlayer = PLAYER_2;
} else {
// already was player 2, so change to 1
currentPlayer = PLAYER_1;
}
}
boolean placeCoin(int column) {
boolean coinPlaced = false;
// now we know the column, we need to find the lowest cell in that column that is empty
for (int y = GRID_ROWS-1; y >= 0; y-- ) { // let's start at bottom and move up to reduce computations
// for every cell, test if it is empty
if (grid[column][y].isEmpty()) {
// if found a valid cell, place current players token and exit the loop (break)
grid[column][y].setValue(currentPlayer);
coinPlaced = true;
break;
}
}
return coinPlaced;
}
void checkCoins() {
// scan the array for 4 of the same value in a row
for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row
//grid[x][y]
// I will leave this to you ;)
// keep in mind to check neighbors all you need to do is add or subtract 1 from the x or y value
// however when you are at the edge of the board be careful not to try and look at a neighbor that is off the edge, you will get a null pointer or an array out of bounds exception
if (x+1<GRID_COLUMNS) {
Cell toTheRight = grid[x+1][y];
}
// for each cell you could look at the 3 above the current, 3 below the current, 3 to the right and 3 to the left, 3 diagnal in each direction and then manually try and find 4 adjacent same color cells
// or a bit more complicated is a recursive solution that checks its 8 immediate neighbor and for each that match the center cell run the same function to test its 8 neighbors keeping track of the current inARowCount and returning true when it is found.
// would be a bit hard because you would need to make sure the second cell doesn't follow back to the original cell, and start an endless loop
}
}
}
void draw () {
background(1, 1, 1);
noStroke();
// draw all cells
for (int y=0; y < GRID_ROWS; y++) { // for every vertical row,
for (int x=0; x < GRID_COLUMNS; x++) { // for every horizontal cell in that row
grid[x][y].display(); // draw this cell
}
}
// draw next coin floating above the board, contrain its positon to above the board
fill(PLAYER_COLORS[currentPlayer]);
int currentMouseX = constrain(mouseX, BOARD_PADDING+COIN_RADIUS/2, BOARD_PADDING+(GRID_COLUMNS*CELL_SCALE)-COIN_RADIUS/2);
//currentMouseX = 40*(ceil(abs(currentMouseX/40)));
ellipse(currentMouseX, BOARD_PADDING/2, COIN_RADIUS, COIN_RADIUS);
}
// press any key to rest the game, probably want to test for a certain key here!
void keyPressed () {
setupBlankBoard();
}
// on press attempt to place a coin
void mousePressed() {
int currentMouseX = constrain(mouseX, BOARD_PADDING+COIN_RADIUS/2, BOARD_PADDING+(GRID_COLUMNS*CELL_SCALE)-COIN_RADIUS/2);
// determine what column we are over
int column = (currentMouseX - BOARD_PADDING)/CELL_SCALE;
// if choice is a valid coin placement
if (placeCoin(column)) {
// toggle players if a coin was placed
changePlayers();
// after each placement check win conditions
checkCoins();
}
}
Cell Class
class Cell {
int x, y;
int value; // 0 for empty, 1 for team 1, 2 for team 2 (constants defined at top of main file)
Cell(int x, int y) {
// default constructor to create empty cell
this(x, y, EMPTY_CELL);
}
// allows cell value to be set at creation
Cell(int x, int y, int value) {
this.x = x;
this.y = y;
this.value = value;
}
boolean setValue(int value){
value = constrain(value, EMPTY_CELL,PLAYER_2); // keeps it from setting a value outside of our range
if(this.value == EMPTY_CELL){
this.value = value;
return true; // placed
}
return false; // was not able to place it as there is already a value
}
boolean isEmpty(){
return this.value == EMPTY_CELL;
}
void display() {
fill(0, 0, 255);
rect(this.x, this.y, CELL_SCALE, CELL_SCALE);
// Draw Circle color based on current value, could simply just put fill(playerColors[this.value]); but this seems a bit more clear
if(this.value == EMPTY_CELL){
fill(PLAYER_COLORS[EMPTY_CELL]);
}else if(this.value == PLAYER_1){
fill(PLAYER_COLORS[PLAYER_1]); // red
}else if(this.value == PLAYER_2){
fill(PLAYER_COLORS[PLAYER_2]); // yellow
}
ellipse(this.x + CELL_SCALE/2, this.y + CELL_SCALE/2, COIN_RADIUS, COIN_RADIUS);
}
}

Adding to vector of objects with attributes determined by constantly changing values

For this issue, I have done research, but I am not sure I have been searching for the right terms? Maybe someone can help...
I am writing an app in openframeworks that is basically a sound visualizer. What I am trying to do is have the program create and draw a rectangle at a certain point, the height of which is determined by the frequency of audio input at the moment of the rectangle's creation. Then, I want the program to draw another rectangle next to it, the height of which determined by the frequency at THAT moment, so forth and so on. (I am also planning to pan this to the left every time, so that it is creating a very long chain of rectangles).
The rectangles are going to look like buildings (drawing a city scape as it goes), so I have created a class of them with very simple attributes: position, height, etc. and the main work (correct me if I am wrong) is going to be in the main part of the application.
The problems I have been having are in the drawing of the object and then having its height correspond to a frequency. Also, I don't want the height of the rectangle to change once it is created, so I am having trouble getting that to happen correctly. Right now, I have only managed to create a single large rectangle that flickers up and down with the sound input.
I am not entirely sure how I should be adding to the vector an object with the correct attributes each second and having that instance of the object's property stay static.
I am not sure if I am asking the right questions correctly, but maybe someone can help?
Here is the top level of the app code:
#include "testApp.h"
//--------------------------------------------------------------
void testApp::setup(){
ofSetFrameRate(60);
ofBackground(0,30,60);
//sound stream setup and such
ofSoundStreamSetup(0,2,this, 44100, BUFFER_SIZE, 4);
left = new float[BUFFER_SIZE];
right = new float[BUFFER_SIZE];
FFTanalyzer.setup(44100, BUFFER_SIZE/2, 1);
FFTanalyzer.peakHoldTime = 15; // hold longer
FFTanalyzer.peakDecayRate = 0.95f; // decay slower
FFTanalyzer.linearEQIntercept = 0.9f; // reduced gain at lowest frequency
FFTanalyzer.linearEQSlope = 0.01f; // increasing gain at higher frequencies
numOctaves=1;
//control panel setup
panel.setup("control", 770, 0, 300, 150);
panel.addPanel("fft settings", 1, false);
panel.setWhichPanel("fft settings");
panel.addSlider("Number of Sub Octaves","NUM_OCT", 1, 1,12, false);
//set up buildings
for (int i = 0; i < bldgs.size() ; i+= 20){
}
}
//--------------------------------------------------------------
void testApp::update(){
panel.update();
if(numOctaves != panel.getValueI("NUM_OCT")){
numOctaves = panel.getValueI("NUM_OCT");
panel.setValueI("NUM_OCT", numOctaves,0);
FFTanalyzer.setup(44100, BUFFER_SIZE/2, numOctaves);
}
}
//--------------------------------------------------------------
void testApp::draw(){
panel.draw();
static int index=0;
float avg_power = 0.0f;
/* do the FFT */
myfft.powerSpectrum(0,(int)BUFFER_SIZE/2, left,BUFFER_SIZE,&magnitude[0],&phase[0],&power[0],&avg_power);
for (int i = 0; i < (int)(BUFFER_SIZE/2); i++){
freq[i] = magnitude[i];
}
FFTanalyzer.calculate(freq);
float binDrawWidth = (ofGetWidth()-20)/FFTanalyzer.nAverages;
//float bldgHeighTemp;
for (int i = 0; i < 1000 ; i+=30){
for (int f = 0; f < (int)(BUFFER_SIZE/2); f++){
bldg temp;
freqs[i] = freq[f]*-6;
temp.bldgPosX = i;
temp.bldgPosY = ofGetHeight()/2;
temp.bldgWidth = 30;
temp.bldgHeight = freqs[i];
temp.draw();
bldgs.push_back(temp);
}
}
}
//--------------------------------------------------------------
void testApp::keyPressed(int key){
}
//--------------------------------------------------------------
void testApp::keyReleased(int key){
}
//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){
}
//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){
panel.mouseDragged(x,y,button);
}
//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){
panel.mousePressed(x,y,button);
}
//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){
panel.mouseReleased();
}
//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
}
//--------------------------------------------------------------
void testApp::audioReceived (float * input, int bufferSize, int nChannels){
// samples are "interleaved"
for (int i = 0; i < bufferSize; i++){
left[i] = input[i*2];
right[i] = input[i*2+1];
}
}
[edit] testapp.h
#ifndef _TEST_APP
#define _TEST_APP
#include "ofMain.h"
#include "fft.h"
#include "FFTOctaveAnalyzer.h"
#include "ofxControlPanel.h"
#include "bldg.h"
#define BUFFER_SIZE 512
class testApp : public ofBaseApp{
public:
void setup();
void update();
void draw();
void keyPressed (int key);
void keyReleased(int key);
void mouseMoved(int x, int y );
void mouseDragged(int x, int y, int button);
void mousePressed(int x, int y, int button);
void mouseReleased(int x, int y, int button);
void windowResized(int w, int h);
void audioReceived (float * input, int bufferSize, int nChannels);
ofxControlPanel panel;
int numOctaves;
FFTOctaveAnalyzer FFTanalyzer;
float * left;
float * right;
int bufferCounter;
fft myfft;
float magnitude[BUFFER_SIZE];
float phase[BUFFER_SIZE];
float power[BUFFER_SIZE];
float freq[BUFFER_SIZE/2];
vector <float> freqs;
vector <bldg> bldgs;
};
#endif
what isn't working is that it isn't creating single instances of the object and/or each instance's attributes are constantly changing according to frequency, which isn't the behavior I'm trying to achieve; what I want is for each object to be created at a certain height and stay that way, then a new ones created next to it so forth and so on, like every second or so.
Make any sense? I am still trying to figure out how to ask the right questions.
Thanks,
Bree
Ok as far as I understand your problem you need to do this, maybe this helps:
Try to store pointers in your bldgs vector
vector<bldg*> bldgs;
and add new objects to it with
bldg* temp;
freqs[i] = freq[f]*-6;
temp->bldgPosX = i;
temp->bldgPosY = ofGetHeight()/2;
temp->bldgWidth = 30;
temp->bldgHeight = freqs[i];
bldgs.push_back(temp);
remove the draw method from here and draw every bldg on every frame. add this to your draw method:
for(int i = 0; i < bldgs.size(); i++)
{
bldgs[i]->draw();
}
it would be a better practise to move everything not directly related to draw to put in the update method of your testApp. so for example move your creation and filling the vector stuff to the update method.

Resources