C - Minesweeper AI not clicking tile - c

The game is Minesweeper. The AI I am trying to implement will take an instance of the game Minesweeper that is already running on a machine (this case Windows 7), get the rectangular dimensions of the windows, compute the location of the window, push it to the foreground of the screen, click the top left square then go through the fancy algorithms to decide which square is a mine and which is clear.
Recently, I have been able to get the handling of the window and cursor correct only after implementing a MessageBox before the first tile is clicked.
With the message box, the program will push the Minesweeper window to the front, generate the message box, after the enter button on the message box is clicked the program will position the cursor on the correct top left square then stop... In the code after the message box, there is a commented out section which, when uncommented, iterates over the entirety of the Minesweeper window and marks it as a mine (flag!) - which tells me it does register the ClickTile correctly for mines - but not clear areas. After this the program is suppose to click the first tile in the top left corner. All that happens is the mouse flickering.
The correct behavior is to push the Minesweeper window to the front, generate the message box, after the message box is clicked, it should click the top left square within the game.
The question is, why is it not registering the LEFTMOUSEDOWN and only the RIGHTMOUSEDOWN in the ClickTile?
UiInfo.h
#ifndef UiInfo_
#define UiInfo_
#include <Windows.h>
struct UiInfo {
float scale;
POINT start;
int tile_size;
};
extern struct UiInfo * ui_info;
void UiInfo_initialize(struct UiInfo * ui_info,
float scale,
POINT start,
int tile_size);
#endif
UiInfo.c
#include "UiInfo.h"
void UiInfo_initialize(struct UiInfo * ui_info,
float scale,
POINT start,
int tile_size) {
ui_info->scale = scale;
ui_info->start = start;
ui_info->tile_size = tile_size;
}
main.c
void ClickTile(int is_mine, int x, int y) {
INPUT input = {0};
int xpos = (int)(ui_info->start.x + ui_info->tile_size * x);
int ypos = (int)(ui_info->start.y + ui_info->tile_size * y);
SetCursorPos(xpos, ypos);
input.type = INPUT_MOUSE;
if(is_mine) {
input.mi.dwFlags = MOUSEEVENTF_RIGHTDOWN;
}
else {
input.mi.dwFlags = MOUSEEVENTF_LEFTDOWN;
}
SendInput(1, &input, sizeof(INPUT));
ZeroMemory(&input, sizeof(INPUT));
input.type = INPUT_MOUSE;
if(is_mine) {
input.mi.dwFlags = MOUSEEVENTF_RIGHTUP;
}
else {
input.mi.dwFlags = MOUSEEVENTF_LEFTUP;
}
SendInput(1, &input, sizeof(INPUT));
}
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow) {
float scale;
POINT start;
RECT rect;
int size;
int x,y;
HWND hwnd_minesweeper = FindWindow(NULL, "Minesweeper");
GetWindowRect(hwnd_minesweeper, &rect);
scale = ((float)(rect.right - rect.left)) / ((float)(74 + 18 * 30));
// the point start is located at the center of the tile in the top left
// corner of the minesweeper map
start.x = ((long)scale) * (38 + 9) + rect.left;
start.y = rect.bottom - ((long)scale) * (38 + 15*18 + 9);
size = (int)scale * 18;
ui_info = (struct UiInfo *)malloc(sizeof(struct UiInfo));
UiInfo_initialize(ui_info, scale, start, size);
SetForegroundWindow(hwnd_minesweeper);
MessageBox(NULL, "A", "B", MB_OK);
/*for(y = 0; y < 16; y++) {
for(x = 0; x < 30; x++) {
Sleep(10);
ClickTile(1,x,y);
}
}*/
//Click the mine in the top left corner to start the game
ClickTile(0, 0, 0);
return 0;
}

I'll take three guesses.
Guess Number 1
You need to use a timer to allow your mouse movement and mouse clicks time to take effect. This involves starting a timer on app launch, and then implementing a state machine that generates new mouse events when the timer fires, e.g.
// start the timer in some initialization function
SetTimer( 75, 5, NULL );
// handler function to update the overall state
void CMainFrame::OnTimer(UINT nIDEvent)
{
switch ( m_State )
{
case STATE_IDLE: // do nothing until the user tells us to start
case STATE_DONE: // do nothing after the game is finished
break;
case STATE_START: // the first click to get things started
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[1][1].mousex, m_Square[1][1].mousey, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTUP , 0, 0, 0, 0 );
m_State = STATE_MOVING;
break;
case STATE_MOVING: // all the fun happens here
if ( !MakeAMove() )
{
m_State = STATE_DONE;
Invalidate();
}
break;
}
CFrameWnd::OnTimer(nIDEvent);
}
Guess Number 2
The mouse coordinate system is 65536 x 65536 regardless of screen resolution. So you need to scale the mouse coordinates to convert to pixel locations. Here's one way to compute the scale factors
HDC hdc = ::GetDC( NULL );
CDC *dc = CDC::FromHandle( hdc );
CWnd *wnd = dc->GetWindow();
wnd->GetClientRect( &rect );
xscale = 65535.0 / (double)rect.right;
yscale = 65535.0 / (double)rect.bottom;
Guess Number 3
You need to move the mouse to the correct screen coordinates before generating the mouse click event, e.g.
// the following code goes in some initialization function that knows the location of the minesweeper window
// note that screenx and screeny are the pixel coordinates of the center point of each square and these
// are converted to mouse coordinates for each square
for ( y = 1; y <= m_MaxY; y++ )
for ( x = 1; x <= m_MaxX; x++ )
{
m_Square[y][x].mousex = (int)(m_Square[y][x].screenx * xscale + 0.5);
m_Square[y][x].mousey = (int)(m_Square[y][x].screeny * yscale + 0.5);
}
// this code generates a left click in a given square
mouse_event( MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, m_Square[y][x].mousex, m_Square[y][x].mousey, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0 );
mouse_event( MOUSEEVENTF_LEFTUP , 0, 0, 0, 0 );

Related

C/ncurses/pads - how to deal at the edges of a pad shown on stdscr with prefresh

I am new to c coding and trying out this and that. Recently I stumbled over ncurses and am experimenting with it.
In Dan Gookin's "guide to programming with ncurses". I saw pads and their properties and functionality and now I want to archive the following:
I want to create a pad of a by far bigger size than the screen, respectively, an outcut of stdscr, so the screen shows parts of the pad. The advantage compared with other solutions is that I don't need to mess around with offsets. you can just call the function
prefresh(padname, y, x, min_y, min_x, max_y, max_y);
which refreshes the screen stdscr and shows a part of the before prepared pad content in a "cutout" from min_y,min_x to max_y,max_x. The offset to the upper left corner is y,x.
So later I want to move around the pad in both directions left-right and up-down. I filled a pad with some dummy data ("Eat my pants" and an incremented number) and moved it around with simple for-loops. It worked out smoothly, but I realized a flaw. At the edges of the pad the information is "cut" and I cannot find a solution to make the wrap. If the offset is negative, prefresh does just through ERR, and the screen stays empty, while if the offset is too big the left or the lower boundaries came into view. That is generally not a problem, but I want to archive a solution where I can get control over the content to make it become something like an edgeless object. So if moving the pad too the left the right side shall be moved into view while and vice versa. Same for up and down. So like a cylinder or a pseudo-ball in a game world.
Anybody an idea how to achieve that with a pad shown on the stdscr with ncurses in a C environment?
Here's my test file (the commented and the lower parts are for testing, respectively, only to check out how scrolling in subwindows works. You will see the lower boundary coming into view after pressing a few times any key.
#include <ncurses.h>
void draw_borders(WINDOW *screen); // some test on winndows only
int main(int argc, char *argv[]) {
int parent_x, parent_y, new_x, new_y;
int left_size = 10; // bad name, means height of the lower windows
initscr();
noecho();
curs_set(FALSE);
// init color use and define two colorpairs
start_color();
init_pair(1,COLOR_WHITE,COLOR_BLUE);
init_pair(2,COLOR_BLACK, COLOR_YELLOW);
init_pair(3,COLOR_BLACK, COLOR_CYAN);
// get our maximum window dimensions
getmaxyx(stdscr, parent_y, parent_x);
// set up initial windows
// WINDOW *field = newwin(parent_y - left_size, parent_x, 0, 0);
// but main screen will be stdscr showing an
// outcut of a padn so init pad here bigger than a screen
WINDOW *mainscreen;
mainscreen = newpad(500,1000);
if (mainscreen == NULL) {
endwin();
puts("Sorry, unable to allocate mainscreen");
return 1;
}
addstr("Added the new pad. Filling.\n");
// fill the pad
for ( int i = 0 ; i < 500000 ; i++) {
wprintw(mainscreen, " Eat my shorts %4d ", i);
}
addstr("Press key to update screen.\n");
getch();
// this is to checkout what happens if the right edge comes into view
// for (int x = 1000-parent_x ; x < 1000-parent_x + 10; x++) {
// prefresh(mainscreen, 0, x, 0 , 0, parent_y - left_size -1, parent_x-1);
// // napms(100);
// getch();
// }
// lower edge coming into view
for (int y = 0 ; y < 10 ; y++) {
prefresh(mainscreen, 500 - parent_y + left_size + y,
0, 0 , 0, parent_y - left_size -1, parent_x-1);
// napms(100);
getch();
}
// only to checkout how subwindows work
WINDOW *left = newwin(left_size, parent_x/2, parent_y - left_size, 0);
WINDOW *right = newwin(left_size, parent_x/2, parent_y - left_size, parent_x/2);
WINDOW *subwindow;
//assign color to windows
//wbkgd(field,COLOR_PAIR(1));
wbkgd(left,COLOR_PAIR(2));
wbkgd(right,COLOR_PAIR(3));
// draw our borders
//draw_borders(field);
draw_borders(left);
draw_borders(right);
// simulate a loop
// while(1) {
// getmaxyx(stdscr, new_y, new_x);
// if (new_y != parent_y || new_x != parent_x) {
// parent_x = new_x;
// parent_y = new_y;
// wresize(field, new_y - left_size, new_x);
// wresize(left, left_size, new_x/2);
// wresize(right, left_size, new_x/2);
//
// mvwin(left, new_y - left_size, 0);
// mvwin(right, new_y - left_size, parent_x/2);
//
//
//
// wclear(stdscr);
// wclear(field);
// wclear(left);
// wclear(right);
//
// draw_borders(field);
// draw_borders(left);
// draw_borders(right);
//
// }
// wresize(field,parent_y - left_size, parent_x);
wresize(left, left_size, parent_x/2);
wresize(right, left_size, parent_x/2);
mvwin(left, parent_y - left_size, 0);
mvwin(right, parent_y - left_size, parent_x/2);
// wclear(stdscr);
// wclear(field);
wclear(left);
wclear(right);
// draw_borders(field);
// draw_borders(left);
draw_borders(right);
// draw to our windows
mvwprintw(left, 1, 1, "Left. ");
mvwprintw(right, 1, 1, "Right");
// refresh each window
refresh();
// wrefresh(field);
wrefresh(left);
wrefresh(right);
subwindow = derwin(left, left_size-2, (parent_x/2)-2, 1, 1);
// scroll newly setup subwindow or not
scrollok(subwindow, TRUE);
for (int i = 0 ; i < 20 ; i++) {
waddstr(subwindow, "This is just some boring placeholder text. ");
napms(100);
wrefresh(subwindow);
}
for (int i = 0 ; i < 3 ; i++) {
scroll(subwindow);
napms(100);
wrefresh(subwindow);
}
for (int i = 0 ; i < 1 ; i++) {
wscrl(subwindow,-3);
napms(100);
wrefresh(subwindow);
}
// }
getch();
// clean up
// delwin(field);
delwin(left);
endwin();
return 0;
}
void draw_borders(WINDOW *screen) {
int x, y, i; getmaxyx(screen, y, x);
// 4 corners
mvwprintw(screen, 0, 0, "+");
mvwprintw(screen, y - 1, 0, "+");
mvwprintw(screen, 0, x - 1, "+");
mvwprintw(screen, y - 1, x - 1, "+");
// sides
for (i = 1; i < (y - 1); i++) {
mvwprintw(screen, i, 0, "|");
mvwprintw(screen, i, x - 1, "|");
}
// top and bottom
for (i = 1; i < (x - 1); i++) {
mvwprintw(screen, 0, i, "-");
mvwprintw(screen, y - 1, i, "-");
}
}

How to get a sketch to work within another sketch that is using screens?

I am working a project that involves screens. I want to be able to use the number keys to which screens, which will take the user into an interactive part of the sketch.
I began working on one of the interactive parts of the program in a separate sketch. Here is that sketch:
float x, y, r, g, b, radius;
int timer;
void setup() {
size(500, 500);
background(255);
noStroke();
smooth();
}
void draw() {
Zon();
}
void Zon(){
// use frameCount to move x, use modulo to keep it within bounds
x = frameCount % width;
// use millis() and a timer to change the y every 2 seconds
if (millis() - timer >= 8000) {
y = random(height);
timer = millis();
}
// use frameCount and noise to change the red color component
r = noise(frameCount * 0.01) * 255;
// use frameCount and modulo to change the green color component
g = frameCount % 1;
// use frameCount and noise to change the blue color component
b = 255 - noise(1 + frameCount * 0.025) * 255;
// use frameCount and noise to change the radius
radius = noise(frameCount * 0.01) * mouseX;
color c = color(r, g, b);
fill(c);
ellipse(x, y, radius, radius);
}
This was the code for the separate sketch. I want to be able to put this sketch into the my actual project, but it is not functioning the way it does in the separate sketch. Can someone explain to me why this is?
I want there to be a white background and for the ellipse to move across the screen, leaving behind a trail. Is it not working because the background is running over and over again, erasing the trail in the process?
When I remove the background(255); it kind of work, except its running on the menu screen, which I don't want.
Here the actual project code:
final int stateMenu = 0;
final int GreenBox = 3;
int state = stateMenu;
float x, y, r, g, b, radius;
int timer;
PFont font;
PFont Amatic;
void setup()
{
size(800, 700);
smooth();
font = createFont("ARCARTER-78.vlw", 14);
textFont(font);
//Amatic = createFont("Amatic-Bold.ttf",60);
//textFont(Amatic);
frameRate(15);
}
void draw()
{
// the main routine. It handels the states.
// runs again and again
switch (state) {
case stateMenu:
showMenu();
break;
case GreenBox:
handleGreenBox();
break;
default:
println ("Unknown state (in draw) "
+ state
+ " ++++++++++++++++++++++");
}
}
void keyPressed() {
// keyboard. Also different depending on the state.
switch (state) {
case stateMenu:
keyPressedForStateMenu();
break;
case GreenBox:
keyPressedForGreenBox();
}
}
void keyPressedForStateMenu() {
switch(key){
case '3':
state = GreenBox;
break;
default:
// do nothing
break;
}
}
void keyPressedForGreenBox(){
switch(key) {
default:
state = stateMenu;
break;
}
}
void showMenu() {
background(255);
fill(0);
textSize(45);
//textFont(Amatic);
text(" Music Box ", 330, 250, 3);
textSize(14);
text("Press 3 for Green", 350, 350);
}
void handleGreenBox() {
Zon();
}
void Zon(){
background(255);
noStroke();
smooth();
// use frameCount to move x, use modulo to keep it within bounds
x = frameCount % width;
// use millis() and a timer to change the y every 2 seconds
if (millis() - timer >= 8000) {
y = random(height);
timer = millis();
}
// use frameCount and noise to change the red color component
r = noise(frameCount * 0.01) * 255;
// use frameCount and modulo to change the green color component
g = frameCount % 1;
// use frameCount and noise to change the blue color component
b = 255 - noise(1 + frameCount * 0.025) * 255;
// use frameCount and noise to change the radius
radius = noise(frameCount * 0.01) * mouseX;
color c = color(r, g, b);
fill(c);
ellipse(x, y, radius, radius);
}
Can someone help me fix this issue, please?
In your original code you're not clearing the background, but in the merged code version you are:
void Zon(){
background(255);
...
and you don't want that as you pointed out.
The only other issue is that you still need to call background(255), but just once, when you switch from the menu state to the GreenBox state:
void keyPressedForStateMenu() {
switch(key) {
case '3':
state = greenBox;
//clear the menu once when moving into greenBox
background(255);
break;
default:
// do nothing
break;
}
}
You've done well in identifying the issue and how solve it partially, just needed to clear the background when changing modes.
Your code will look like this:
final int stateMenu = 0;
final int greenBox = 3;
int state = stateMenu;
float x, y, r, g, b, radius;
int timer;
PFont font;
PFont Amatic;
void setup()
{
size(800, 700);
smooth();
//maybe loadFont ?
font = createFont("ARCARTER-78.vlw", 14);
textFont(font);
//Amatic = createFont("Amatic-Bold.ttf",60);
//textFont(Amatic);
frameRate(15);
}
void draw()
{
// the main routine. It handels the states.
// runs again and again
switch (state) {
case stateMenu:
showMenu();
break;
case greenBox:
handlegreenBox();
break;
default:
println ("Unknown state (in draw) "
+ state
+ " ++++++++++++++++++++++");
}
}
void keyPressed() {
// keyboard. Also different depending on the state.
switch (state) {
case stateMenu:
keyPressedForStateMenu();
break;
case greenBox:
keyPressedForgreenBox();
}
}
void keyPressedForStateMenu() {
switch(key) {
case '3':
state = greenBox;
//clear the menu once when moving into greenBox
background(255);
break;
default:
// do nothing
break;
}
}
void keyPressedForgreenBox() {
switch(key) {
default:
state = stateMenu;
break;
}
}
void showMenu() {
background(255);
fill(0);
textSize(45);
//textFont(Amatic);
text(" Music Box ", 330, 250);
textSize(14);
text("Press 3 for Green", 350, 350);
}
void handlegreenBox() {
Zon();
}
void Zon() {
//don't clear the buffer continuously if you want to leave trails
// background(255);
noStroke();
smooth();
// use frameCount to move x, use modulo to keep it within bounds
x = frameCount % width;
// use millis() and a timer to change the y every 2 seconds
if (millis() - timer >= 8000) {
y = random(height);
timer = millis();
}
// use frameCount and noise to change the red color component
r = noise(frameCount * 0.01) * 255;
// use frameCount and modulo to change the green color component
g = frameCount % 1;
// use frameCount and noise to change the blue color component
b = 255 - noise(1 + frameCount * 0.025) * 255;
// use frameCount and noise to change the radius
radius = noise(frameCount * 0.01) * mouseX;
color c = color(r, g, b);
fill(c);
ellipse(x, y, radius, radius);
}
If you want to be able to draw a background and have a "trail" drawn on top of that background, then you have two options:
Option 1: Create a data structure that contains everything you need to draw, and then draw everything in that data structure every frame. This could be as simple as an ArrayList<PVector>, or you might be better off creating your own classes that encapsulate everything you need to know in order to draw everything in a frame.
Option 2: Create a PGraphics that you draw your trail to. Then draw your background to the screen, and then draw that PGraphics to the screen. Info on this approach can be found in the reference.
Which approach you take really depends on you. I recommend putting together a little example that tests out each to see which one makes more sense to you. Then you can post an MCVE of that small example if you get stuck. Good luck.

Mouse click changing the screen face in OpenGL

I'm programming a code that when the user click on the screen It marks a point and when he continues clicking on other points on the screen It'll be linking the points with lines.
I don't know why but I'm working with two different screen plan. When I click with the mouse It marks a point in the screen that I only see when I'm with the left mouse button clicked and when the button isn't clicked I see a clean screen.
My code that marks the points and link them with lines:
void exerciseThree(int x, int y){
write("Exercise Three", -5, 18);
float cx = 0, cy = 0;
if(x != 0 && y != 0 && isFinished == false){
glPushMatrix();
glPointSize(6.0f);
//Draw the points
glBegin(GL_POINTS);
cx = ((x * (maxx - minx)) / width) + minx;
cy = (((height - y) * (maxy - miny)) / height) + miny;
glVertex2f(cx , cy);
glEnd();
if(lastCx != 0 && lastCy != 0){
glBegin(GL_LINE_STRIP);
glVertex2f(lastCx , lastCy);
glVertex2f(cx , cy);
glEnd();
}
lastCx = cx;
lastCy = cy;
glPopMatrix();
}
write("Press 0 (zero) to come back.", -10, -18);
}
The mouse function:
void mouse(int button, int state, int x, int y){
switch(button){
case GLUT_LEFT_BUTTON:
if( option == 3){
exerciseThree(x, y);
projecao();
glutSwapBuffers();
}
break;
}
}
I know that I should handle the GLUT_DOWN and GLUT_UP of the mouse, but does exist a way of work with only one screen face?
You're only seeing something on the screen when you click because that's the only time you are drawing to and updating the buffer. You should just update the list of x/y coordinates when the mouse is clicked. However, you should draw the points and call glutSwapBuffers() every time in your main loop so that they are always on screen regardless of a button press.
The flow should be something like the following pseudo code:
ArrayOfPoints[];
while(Running)
{
CheckForMouseInput(); // Update array of points to draw if pressed
DrawPoints(); // Use same code draw code from exerciseThree()
glutSwapBuffers(); // Update video buffer
}

Multilayer graphics using GDI

I'm using VisualStudio 2010, coding in C++/CLI, and doing all the graphics by GDI. I have a little app that plot continuously a Gaussian curve with some noise added. Every point is added real-time just like I pointed in this post.
Now, my task is to create a little colored area that I can shrink and increase to select a portion of the plot and do some math.
This kind of task is managed by a MouseMove event just like that:
System::Void Form1::pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
//Recalculate the position of the area,
//clean up the old one and redraw a new.
}
It works actually but I'm experiencing a bit graphic "bug".
As you can see, while I'm moving the area, everything under it is been deleted. The grid is here simply because it is static and I'm refreshing it everytime the green area is redrawn.
Actually it is not a bug, for sure it must go like that. To me, it is kinda obvious. I called it like that because it is not what I'm expecting.
I'm asking if there is a way to the green area as if it is upon a different layer. In this way, I would be able to move the green area while the plot is running without being erased.
I tried handling 2 HDC variables and plot the graph and the grid on the first one and the green area on the second one, but it seems not working.
Do you have some nice idea to get through this bad ( to me ) behaviour - maybe with some multilayer thing or some other fancy solutions - or should I give up and waiting for replotting?
Thanks everyone will give me an answer! :)
EDIT:
Here is how I draw my dataseries:
for(int i = 1; i<=1000; i++ ) {
Gauss[i] = safe_cast<float>(Math::Round( a*s*Math::Exp(-Math::Pow(((0.01*1*(i))-portante), 2)/b), 2));
Rumore[i] = safe_cast<float>(Math::Round(r*generatore->NextDouble(), 2));
SelectObject(hdcPictureBox, LinePen);
MoveToEx(hdcPictureBox, i-1+50, 500-convY*(Gauss[i-1]+Rumore[i-1])+50, NULL);
LineTo(hdcPictureBox, i+50, 500-convY*(Gauss[i]+Rumore[i])+50);
e1 = (i+k)%1000; //Buffer
if(i>DXX-54 && i<SXX-54) {
//ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
label1->Text = Convert::ToString(i);
label1->Refresh();
SelectObject(hdcPictureBox, ErasePen1);
}
else {
SelectObject(hdcPictureBox, ErasePen);
}
//HPEN ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
MoveToEx(hdcPictureBox, e1+50, 500-convY*(Gauss[e1]+Rumore[e1])+50, NULL);
LineTo(hdcPictureBox, e1+1+50, 500-convY*(Gauss[e1+1]+Rumore[e1+1])+50);
}
where DXX and SXX are the X-coordinates of areas - DXX starting, SXX ending.
This is how I'm handling the MouseMove. Do_Chan and Do_Clean are essentially the same thing. Do_Clean draws a bigger area with the background color to erase the old area and allowing Do_Chan to draw a new one.
System::Void Form1::pictureBox1_MouseMove(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e) {
if(e->Button == System::Windows::Forms::MouseButtons::Left) {
double span100 = (SXX-DXX)*85/100;
if (e->X > DXX+((SXX-DXX)/2)-15 && e->X < DXX+((SXX-DXX)/2)+15 && (e->Y >30 && e->Y <50)
|| e->X >DXX+((SXX-DXX)/2)-span100/2 && e->X < DXX+((SXX-DXX)/2)+span100/2 && (e->Y >50 && e->Y <550)) {
HBRUSH brush = CreateSolidBrush(RGB(245,255,250));
Do_Clean(hdcPictureBox, DXX, SXX, brush);
double spawn = SXX-DXX;
DXX = e->X - spawn/2;
SXX = e->X + spawn/2;
if(DXX < 50) {
DXX = 51;
}
if(SXX >1050 ) {
SXX = 1049;
}
spawn = SXX - DXX;
CXX = DXX + spawn/2;
HBRUSH brush1 = CreateSolidBrush(RGB(166,251,178));
Do_Chan(hdcPictureBox2, DXX, SXX, brush1);
int k = 4;
int e1 = 0;
for(int i = 1; i<=1000; i++) {
SelectObject(hdcPictureBox, LinePen);
MoveToEx(hdcPictureBox, i-1+50, 500-250*(Gauss[i-1]+Rumore[i-1])+50, NULL);
LineTo(hdcPictureBox, i+50, 500-250*(Gauss[i]+Rumore[i])+50);
e1 = (i+k)%1000; //Buffer
if(i>DXX-54 && i<SXX-54) {
//ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
SelectObject(hdcPictureBox, ErasePen1);
}
else {
SelectObject(hdcPictureBox, ErasePen);
}
//HPEN ErasePen1 = CreatePen(PS_SOLID, 1, RGB(216,191,216));
MoveToEx(hdcPictureBox, e1+50, 500-250*(Gauss[e1]+Rumore[e1])+50, NULL);
LineTo(hdcPictureBox, e1+1+50, 500-250*(Gauss[e1+1]+Rumore[e1+1])+50);
}
}
}
}
As you can see, after I drew the new area, I redraw all the point of the array Gauss+Rumore.
This is how Do_Chan ( Do_Clean is the same ) works:
void Do_Chan(HDC hdc, int dx, int sx, HBRUSH brush) {
//i = 250, y = 50
int y = 50;
int spawn = sx - dx;
HPEN pen = CreatePen(PS_SOLID, 1, RGB(245, 255, 250));
HPEN penC = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
/*Fai il rettangolo */
SelectObject(hdc, pen);
SelectObject(hdc, brush);
POINT punti[4];
punti[0].x = dx;
punti[0].y = y;
punti[1].x = dx +spawn;
punti[1].y = y;
punti[2].x = dx + spawn;
punti[2].y = y+500;
punti[3].x = dx;
punti[3].y = y+500;
Polygon(hdc, punti, 4);
Ellipse(hdc, dx-10, y-20, dx+10, y);
SelectObject(hdc, penC);
MoveToEx(hdc, dx+spawn/2, 50,NULL);
LineTo(hdc, dx+spawn/2, 550);
SelectObject(hdc, pen);
SelectObject(hdc, brush);
Ellipse(hdc, dx-10+spawn/2, y-20, dx+10+spawn/2, y);
SelectObject(hdc, pen);
SelectObject(hdc, brush);
Ellipse(hdc, dx-10+spawn, y-20, dx+10+spawn, y);
//Plot the axis and the grid
}
I have thought of a possible way to do it and every solution had a drawback. For example
creating a thread. It has a drawback of drawing to picturebox dc from a different thread than the one handling the message queue. Not recomended
Another one:
using a timer and for every tick(lets say 16msec) draw. The DXX and SXX will be global variables. In picturebox move event you will only calculate these values(no drawing), also use some critical sections to protect them, and do all the drawing inside tick of timer. This works but you probable encounter some delay if your movement in picturebox is faster than 60fps.
The solution that i came finally was:
Inside your infinite loop:
get the mouse position and state(down or up). In this way you know if the user is dragging the green area and calculate DXX and SXX.
draw three rectangles with FillRect(): from 0 to DXX with picturebox back color, from DXX to SXX with green color and from SXX to the end with picturebox back color to an in memory dc eg hdcMemBackground
draw the grid lines to hdcMemBackground
Use the Point array and the polyline method i told you and in every loop move all your 999 points in the array one place to the left and add one point in the end of the array. To achieve this fill the array once before the infinite loop and inside it do the previous method
BitBlt hdcMemBackground to picturebox dc
Application::DoEvents();
EDIT (some code)
Create your resources once, when form loads, and release them in the end. Your Do_Chan()
creates considerable memory leaks. At form load:
HPEN hLinePenRed = NULL, hLinePenBlack = NULL, hLinePenWhite = NULL, hLinePenBlackDotted = NULL hPenOld; //Global
HBRUSH hBrushGreen = NULL, hBrushWhite = NULL, hBrushOld = NULL; //Global
HBITMAP hBitmap = NULL, hBitmapOld = NULL; //Global
HDC hdcMemBackground = NULL, hdcPicBox = NULL; //Global
//in form load event:
hLinePenRed = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
hLinePenBlack = CreatePen(PS_SOLID, 1, RGB(0, 0, 0));
hLinePenWhite = CreatePen(PS_SOLID, 1, RGB(245, 255, 250));
hLinePenBlackDotted = CreatePen(PS_DOT, 1, RGB(0, 0, 0));
hPenOld = SelectObject(hdcMemBackground, hLinePenRed);
hBrushGreen = CreateSolidBrush(RGB(166, 251, 178));
hBrushWhite = CreateSolidBrush(RGB(245, 255, 250));
hBrushOld = SelectObject(hdcMemBackground, hBrushGreen);
HDC hdc = CreateIC(TEXT("DISPLAY"), NULL, NULL, NULL);
hdcPicBox = GetWindowDC(hwndPicBox);
hdcMemBackground= CreateCompatibleDC(hdc);
hBitmap = CreateCompatibleBitmap(hdc, 1050, 550);
hBitmapOld = SelectObject(hdcMemBackground, hBitmap);
DeleteDC(hdc);
In the end when form closes:
SelectObject(hdcMemBackground, hPenOld);
DeleteObject(hLinePenRed);
DeleteObject(hLinePenBlack);
DeleteObject(hLinePenBlackDotted);
DeleteObject(hLinePenWhite);
SelectObject(hdcMemBackground, hBitmapOld);
DeleteObject(hBitmap);
SelectObject(hdcMemBackground, hBrushOld);
DeleteObject(hBrushGreen);
DeleteObject(hBrushWhite);
DeleteDC(hdcMemBackground);
ReleaseDC(hwndPicBox, hdcPicBox);
How to use FillRect and draw ellipses:
RECT rt;
rt.left = 0; rt.top = 0; rt.right = 1050; rt.bottom = 550;
FillRect(hdcMemBackground, &rt, hBrushWhite);
rt.left = DXX; rt.top = 50; rt.right = SXX; rt.bottom = 550;
FillRect(hdcMemBackground, &rt, hBrushGreen);
SelectObject(hdcMemBackground, hBrushGreen);
SelectObject(hdcMemBackground, hLinePenWhite);
Ellipse(hdcMemBackground, dx-10, y-20, dx+10, y);
Ellipse(hdcMemBackground, dx-10+spawn/2, y-20, dx+10+spawn/2, y);
Ellipse(hdcMemBackground, dx-10+spawn, y-20, dx+10+spawn, y);
//Plot the axis and the grid first and then draw the dotted vertical line
SelectObject(hdcMemBackground, hLinePenBlackDotted);
MoveToEx(hdcMemBackground, dx+spawn/2, 50, NULL);
LineTo(hdcMemBackground, dx+spawn/2, 550);
How to find mouse position and mouse state. This code will be at the beginning of each
iteration to see if user dragged the green area and calculate the new DXX, SXX:
/* It is buggy. My mistake
POINT pt;
GetCursorPos(&pt);
ScreenToClient(hwndPicBox, &pt);
if( pt.x >= 0 && pt.x <= picBoxWidth && pt.y >= 0 && pt.y <= picBoxHeight && (GetAsyncKeyState(VK_LBUTTON) & 0x8000) ){ //the mouse is down and inside picturebox
//do your staff
}
*/
Use instead picturebox mouse down, mouse up and mouse move events:
int isScrollingLeft = false; //global, the left circle
int isScrollingRight = false; //global, the right circle
int isScrollingMiddle = false; //global, the middle circle
System::Void Form1::pictureBox1_MouseDown(....){
//check if e.X and e.Y is inside in one of the three circles and set the
//appropriate isScrolling to true
}
System::Void Form1::pictureBox1_MouseMove(....){
if(isScrollingLeft){
//calculate DXX
}
else if(isScrollingRight){
//calculate SXX
}
else if(isScrollingMiddle){ //if you dont scroll this you dont need it
//
}
else{;} //do nothing
}
System::Void Form1::pictureBox1_MouseUp(....){
isScrollingLeft = false;
isScrollingRight = false;
isScrollingMiddle = false; //if you dont scroll this you dont need it
}
The way to move the points one place to the left:
POINT arrayPnt[1000]; //the array of points to be drawn by Polyline()
//the movement
memmove(&arrayPnt[0], &arrayPnt[1], 999 * sizeof(POINT));
//set the last one
arrayPnt[999].x = X;
arrayPnt[999].y = Y;
//Draw the lines
Polyline(hdcMemBackground, &arrayPnt, 1000);
To draw the number i into the label:
HDC hdcLabel1 = NULL; //global
HFONT hFont = NULL, hFontOld = NULL; //global
RECT rtLabel = NULL; //global
char strLabel1[5]; //global
Initialize once at the beggining like everything else
hdcLabel1 = GetWindowDC(label1Hwnd);
SetBkColor(hdcLabel1, RGB(?, ?, ?)); //i believe you use the color of your form
SetTextColor(hdcLabel1, RGB(255, 255, 255));
hFont = CreateFont(21, 0, 0, 0, /* Bold or normal*/FW_NORMAL, /*italic*/0, /*underline*/0, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, TEXT("Arial")); //21 is i believe the 16 size in word
hFontOld = SelectObject(hdcLabel1, hFont);
rtLabel.top = 0; rtLabel.left = 0;
rtLabel.right = label1.Width; rtLabel.bottom = label1.Height;
and in the for loop draw the string into the label1
sprintf(strLabel1, "%d", i); //it is toooo slow. I have to think something better
DrawTextEx(hdcLabel1, strLabel1, -1, &rtLabel, DT_VCENTER | DT_SINGLELINE | DT_LEFT, NULL);
In the end ofcource release resources
SelectObject(hdcLabel1, hFontOld);
DeleteObject(hFont);
hFont = NULL;
ReleaseDC(label1Hwnd, hdcLabel1);
If you have any problems do comment.
valter

How would one draw to the sub display of a ds as if it was a framebuffer?

I need to draw raw pixel data to the Nintendo DS's "sub" screen, such as if I was drawing to the main screen in "framebuffer" mode or "Extended Rotation" mode. How can I do this with the current version of libnds (which seems to place restrictions on the use of VRAM_C)?
#include <nds.h>
int main(void)
{
int x, y;
//set the mode to allow for an extended rotation background
videoSetMode(MODE_5_2D);
videoSetModeSub(MODE_5_2D);
//allocate a vram bank for each display
vramSetBankA(VRAM_A_MAIN_BG);
vramSetBankC(VRAM_C_SUB_BG);
//create a background on each display
int bgMain = bgInit(3, BgType_Bmp16, BgSize_B16_256x256, 0,0);
int bgSub = bgInitSub(3, BgType_Bmp16, BgSize_B16_256x256, 0,0);
u16* videoMemoryMain = bgGetGfxPtr(bgMain);
u16* videoMemorySub = bgGetGfxPtr(bgSub);
//initialize it with a color
for(x = 0; x < 256; x++)
for(y = 0; y < 256; y++)
{
videoMemoryMain[x + y * 256] = ARGB16(1, 31, 0, 0);
videoMemorySub[x + y * 256] = ARGB16(1, 0, 0, 31);
}
while(1)
{
swiWaitForVBlank();
}
}
Here is a simple example which creates a 16 bit frame buffer on the main and sub screens and fills each with either red or blue.

Resources