Discussion:
Help by chatbot
Add Reply
Stefan Ram
2024-09-07 16:55:41 UTC
Reply
Permalink
Hey everyone! Today, I threw together a C++ program for Windows
with a little help from a chatbot. It's still got some hiccups
and bugs, but hey, it's showing some basic vibes!

I've got a bit of a clue about C++, but the last time I dabbled
a bit in Windows programming was ages ago. Honestly, I wouldn't
have been able to make this happen without some guidance!

The chatbot kicked things off for me with a starter program, and
now I can tweak it to get closer to what I really want to create.

(I basically told the chatbot that I wanted a C++ program for
Windows where a black circle moves towards a red one. Then I went
over some details about how I wanted it coded, and together with
the chatbot, I squashed the bugs until it was running.)

The end condition for the loop still ain't working.
The black circle should stop moving when it hits the red circle.
Instead, the program's stuck in an endless loop (it'll freeze!).
But that's chill. I can handle it later.

I just added the "Sleep(60);" to slow things down so that it does
not run too fast, but it's actually intended without this line.

Manual: Hit Alt-R or just click "Run." You should see two
circles pop up in random spots, with the black one moving
towards the red one. Then, the program will freeze up,
and you'll need to force quit it from outside. (Closing the
console seems to work.)

Some compiler options used (the chatbot explained that to me, too!):

. . . main.cpp -static -mwindows -lquadmath -luser32 -lgdi32

. main.cpp

#include <windows.h>
#include <cstdlib>
#include <ctime>
#include <iostream>

const int WIDTH = 800; // Width of the window
const int HEIGHT = 600; // Height of the window

HBITMAP hBitmap;
HDC hdcMem, hdcWindow;
int redCircleX, redCircleY, blackCircleX, blackCircleY;
bool isRunning = false; // Flag to indicate if the simulation is running
HWND hwnd; // Declare hwnd as a global variable

// Define a custom message for the simulation
#define WM_SIMULATE (WM_USER + 1)

// Timing variables
DWORD lastUpdateTime = 0; // Last time the window was updated
const DWORD updateInterval = 50; // Update interval in milliseconds (20 updates per second)

void CreateConsole() {
AllocConsole(); // Allocate a new console for the application
FILE* fp;
freopen_s(&fp, "CONOUT$", "w", stdout); // Redirect stdout to the console
freopen_s(&fp, "CONOUT$", "w", stderr); // Redirect stderr to the console
}

void StartSimulation() {
std::srand(std::time(0));
redCircleX = std::rand() % WIDTH;
redCircleY = std::rand() % HEIGHT;
blackCircleX = std::rand() % WIDTH;
blackCircleY = std::rand() % HEIGHT;

std::cout << "Red Circle Position: (" << redCircleX << ", " << redCircleY << ")" << std::endl;
std::cout << "Black Circle Position: (" << blackCircleX << ", " << blackCircleY << ")" << std::endl;

// Fill bitmap with white
HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
RECT rect = {0, 0, WIDTH, HEIGHT}; // Define RECT here
FillRect(hdcMem, &rect, whiteBrush);
DeleteObject(whiteBrush);

// Draw red circle
HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
SelectObject(hdcMem, redBrush);
Ellipse(hdcMem, redCircleX - 10, redCircleY - 10, redCircleX + 10, redCircleY + 10);
DeleteObject(redBrush);

// Draw black circle
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdcMem, blackBrush);
Ellipse(hdcMem, blackCircleX - 10, blackCircleY - 10, blackCircleX + 10, blackCircleY + 10);
DeleteObject(blackBrush);

isRunning = true; // Set the flag to indicate that the simulation is running
lastUpdateTime = GetTickCount(); // Initialize the last update time
}

void SimulateStep() {
// Check neighboring pixels
if ((blackCircleX > 0 && blackCircleY > 0 && GetPixel(hdcMem, blackCircleX - 1, blackCircleY) == RGB(255, 0, 0)) ||
(blackCircleX < WIDTH - 1 && blackCircleY > 0 && GetPixel(hdcMem, blackCircleX + 1, blackCircleY) == RGB(255, 0, 0)) ||
(blackCircleX > 0 && blackCircleY < HEIGHT - 1 && GetPixel(hdcMem, blackCircleX, blackCircleY - 1) == RGB(255, 0, 0)) ||
(blackCircleX < WIDTH - 1 && blackCircleY < HEIGHT - 1 && GetPixel(hdcMem, blackCircleX, blackCircleY + 1) == RGB(255, 0, 0))) {
std::cout << "Black circle is now adjacent to red circle." << std::endl;
isRunning = false; // Stop the simulation
return; // Exit the step
}

// Move black circle towards red circle
if (blackCircleX < redCircleX) blackCircleX++;
else if (blackCircleX > redCircleX) blackCircleX--;

if (blackCircleY < redCircleY) blackCircleY++;
else if (blackCircleY > redCircleY) blackCircleY--;

// Clear and redraw
HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255));
RECT rect = {0, 0, WIDTH, HEIGHT}; // Define RECT here
FillRect(hdcMem, &rect, whiteBrush); // Use the defined RECT
DeleteObject(whiteBrush);

// Draw red circle
HBRUSH redBrush = CreateSolidBrush(RGB(255, 0, 0));
SelectObject(hdcMem, redBrush);
Ellipse(hdcMem, redCircleX - 10, redCircleY - 10, redCircleX + 10, redCircleY + 10);
DeleteObject(redBrush);

// Draw black circle
HBRUSH blackBrush = CreateSolidBrush(RGB(0, 0, 0));
SelectObject(hdcMem, blackBrush);
Ellipse(hdcMem, blackCircleX - 10, blackCircleY - 10, blackCircleX + 10, blackCircleY + 10);
DeleteObject(blackBrush);

// Check if it's time to update the window
DWORD currentTime = GetTickCount();
if (currentTime - lastUpdateTime >= updateInterval) {
InvalidateRect(hwnd, NULL, TRUE); // Use TRUE to force a full redraw
UpdateWindow(hwnd); // Force the window to repaint immediately
lastUpdateTime = currentTime; // Update the last update time
}

// Debug output for the current positions
std::cout << "Current Black Circle Position: (" << blackCircleX << ", " << blackCircleY << ")" << std::endl;
Sleep(60);
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
switch (msg) {
case WM_PAINT: {
PAINTSTRUCT ps;
BeginPaint(hwnd, &ps);
BitBlt(ps.hdc, 0, 0, WIDTH, HEIGHT, hdcMem, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
break;
}
case WM_DESTROY: {
DeleteObject(hBitmap);
DeleteDC(hdcMem);
PostQuitMessage(0);
break;
}
case WM_COMMAND: {
if (LOWORD(wParam) == 1) { // "Run" menu item
std::cout << "Run command triggered." << std::endl;
StartSimulation(); // Start the simulation
PostMessage(hwnd, WM_SIMULATE, 0, 0); // Post a custom message to start the simulation loop
}
break;
}
case WM_SIMULATE: {
if (isRunning) {
SimulateStep(); // Perform one step of the simulation
PostMessage(hwnd, WM_SIMULATE, 0, 0); // Post the message again for the next iteration
}
break;
}
}
return DefWindowProc(hwnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) {
CreateConsole(); // Create console for debug output

// Create memory device context
hdcMem = CreateCompatibleDC(NULL);
hBitmap = CreateCompatibleBitmap(GetDC(NULL), WIDTH, HEIGHT);
SelectObject(hdcMem, hBitmap);

// Register window class
const char CLASS_NAME[] = "Sample Window Class";
WNDCLASS wc = {};
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.lpszClassName = CLASS_NAME;
RegisterClass(&wc);

// Create the menu
HMENU hMenu = CreateMenu();
AppendMenu(hMenu, MF_STRING, 1, "Run"); // Add "Run" menu item

// Create window
hwnd = CreateWindowEx(0, CLASS_NAME, "Circle Simulation", WS_OVERLAPPEDWINDOW | WS_VISIBLE,
CW_USEDEFAULT, CW_USEDEFAULT, WIDTH, HEIGHT, NULL, hMenu, hInstance, NULL);
ShowWindow(hwnd, nCmdShow);

// Message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}

return 0;
}
Stefan Ram
2024-09-07 17:40:30 UTC
Reply
Permalink
Post by Stefan Ram
// Message loop
MSG msg;
while (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
We seem to have leveled up our game with handling Windows
messages - now the window can actually be closed!

while (true) {
// Check for messages
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
return msg.wParam; // Exit the application
}
}

// Process simulation step if running
if (isRunning) {
SimulateStep(); // Perform one step of the simulation
}
}
R.Wieser
2024-09-09 14:14:08 UTC
Reply
Permalink
Stefan,
Post by Stefan Ram
DispatchMessage(&msg);
if (msg.message == WM_QUIT) {
return msg.wParam; // Exit the application
https://learn.microsoft.com/en-us/windows/win32/learnwin32/window-messages

See WM_DESTROY and PostQuitMessage

(the page is one-of-several, combining into a small tutorial)

Some other stuff is off too: creating a memory DC before entering the
message-loop, but destroying it somewhere inside of it (instead of after the
message-loop exits)

Bottom line: your "chatbot" created something that (with some of your help)
runs, but what I would call "hacked together" (IOW, not something you would
want to show your boss/teacher).

I do hope you enjoyed talking with it to/and creating the program though.

Regards,
Rudy Wieser
Stefan Ram
2024-09-09 14:34:33 UTC
Reply
Permalink
Post by R.Wieser
Some other stuff is off too: creating a memory DC before entering the
message-loop, but destroying it somewhere inside of it (instead of after the
message-loop exits)
Thanks for the heads up! I'll try to keep those pointers in mind
next time I'm tweaking the source code.
Post by R.Wieser
I do hope you enjoyed talking with it to/and creating the program though.
Yeah, it was pretty sweet to see some progress on my little
pet project. I went with Python 'cause it's got those built-in
graphics libraries (turtle and tkinter) that C++ doesn't, but
Python's going to be way too slow for what I'm cooking up. At least
now I've got something to start from without breaking a sweat.

(The endgame is to simulate these robots whose brains level up
through some evolution-type deal. Kinda like "Framsticks", but
way more chill since I'm just doing this as a side hustle when
I'm not crushing it at work or hitting the beach.)

Loading...