· 11 months ago · Oct 23, 2024, 04:26 AM
1==++"2D-Snake-Game.cpp File SourceCode::++==
2#include <windows.h>
3#include <vector>
4#include <ctime>
5#include <cstdlib>
6
7#define WINDOW_WIDTH 800
8#define WINDOW_HEIGHT 600
9#define GRID_SIZE 20
10#define SNAKE_INITIAL_LENGTH 5
11#define MOVE_INTERVAL 100
12
13enum Direction { UP, DOWN, LEFT, RIGHT };
14
15struct Point {
16 int x, y;
17 Point(int _x = 0, int _y = 0) : x(_x), y(_y) {}
18};
19
20class Snake {
21private:
22 std::vector<Point> body;
23 Direction currentDirection;
24 Direction nextDirection;
25 bool growing;
26
27public:
28 Snake() : currentDirection(RIGHT), nextDirection(RIGHT), growing(false) {
29 for (int i = 0; i < SNAKE_INITIAL_LENGTH; ++i) {
30 body.push_back(Point(5 - i, 5));
31 }
32 }
33
34 void move() {
35 currentDirection = nextDirection;
36 Point newHead = body.front();
37 switch (currentDirection) {
38 case UP: newHead.y--; break;
39 case DOWN: newHead.y++; break;
40 case LEFT: newHead.x--; break;
41 case RIGHT: newHead.x++; break;
42 }
43
44 // Ensure the snake wraps around the window area
45 newHead.x = (newHead.x + (WINDOW_WIDTH / GRID_SIZE)) % (WINDOW_WIDTH / GRID_SIZE);
46 newHead.y = (newHead.y + (WINDOW_HEIGHT / GRID_SIZE)) % (WINDOW_HEIGHT / GRID_SIZE);
47
48 body.insert(body.begin(), newHead);
49 if (!growing) {
50 body.pop_back();
51 }
52 growing = false;
53 }
54
55
56
57 void grow() { growing = true; }
58
59 void setDirection(Direction dir) {
60 if ((dir == UP || dir == DOWN) && (currentDirection == LEFT || currentDirection == RIGHT)) {
61 nextDirection = dir;
62 }
63 else if ((dir == LEFT || dir == RIGHT) && (currentDirection == UP || currentDirection == DOWN)) {
64 nextDirection = dir;
65 }
66 }
67
68 bool checkCollision() const {
69 for (size_t i = 1; i < body.size(); ++i) {
70 if (body[0].x == body[i].x && body[0].y == body[i].y) {
71 return true;
72 }
73 }
74 return false;
75 }
76
77 const std::vector<Point>& getBody() const { return body; }
78
79 Direction getCurrentDirection() const {
80 return currentDirection;
81 }
82};
83
84class Game {
85private:
86 Snake snake;
87 Point food;
88 bool gameOver;
89 bool paused;
90 int score;
91
92 HWND hwnd;
93 HDC hdc, memDC;
94 HBITMAP memBitmap;
95 HBRUSH snakeBrush, foodBrush, backgroundBrush;
96
97public:
98 Game(HWND hWnd) : hwnd(hWnd), gameOver(false), paused(true), score(0) {
99 srand(static_cast<unsigned>(time(nullptr)));
100 spawnFood();
101
102 hdc = GetDC(hwnd);
103 memDC = CreateCompatibleDC(hdc);
104 memBitmap = CreateCompatibleBitmap(hdc, WINDOW_WIDTH, WINDOW_HEIGHT);
105 SelectObject(memDC, memBitmap);
106
107 snakeBrush = CreateSolidBrush(RGB(0, 255, 0)); // Red (255, 0, 0)
108 foodBrush = CreateSolidBrush(RGB(255, 0, 0)); // Red (255, 0, 0)
109 backgroundBrush = CreateSolidBrush(RGB(0, 0, 0)); //khaki 196, 178, 137 // black 0,0,0
110 }
111
112 ~Game() {
113 DeleteObject(snakeBrush);
114 DeleteObject(foodBrush);
115 DeleteObject(backgroundBrush);
116 DeleteObject(memBitmap);
117 DeleteDC(memDC);
118 ReleaseDC(hwnd, hdc);
119 }
120
121 void spawnFood() {
122 int gridWidth = WINDOW_WIDTH / GRID_SIZE;
123 int gridHeight = WINDOW_HEIGHT / GRID_SIZE;
124 do {
125 food.x = rand() % gridWidth;
126 food.y = rand() % gridHeight;
127 } while (isSnakeOnPoint(food));
128 }
129
130
131 bool isSnakeOnPoint(const Point& p) const {
132 for (const auto& segment : snake.getBody()) {
133 if (segment.x == p.x && segment.y == p.y) {
134 return true;
135 }
136 }
137 return false;
138 }
139
140 void update() {
141 if (gameOver || paused) return;
142
143 snake.move();
144
145 if (snake.checkCollision()) {
146 gameOver = true;
147 return;
148 }
149
150 if (snake.getBody().front().x == food.x && snake.getBody().front().y == food.y) {
151 snake.grow();
152 do {
153 spawnFood();
154 } while (isSnakeOnPoint(food));
155 score++;
156 }
157 }
158
159 void render() {
160 RECT rect = { 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT };
161 FillRect(memDC, &rect, backgroundBrush);
162
163 // Draw snake body
164 for (const auto& segment : snake.getBody()) {
165 RECT snakeRect = {
166 segment.x * GRID_SIZE,
167 segment.y * GRID_SIZE,
168 (segment.x + 1) * GRID_SIZE,
169 (segment.y + 1) * GRID_SIZE
170 };
171 FillRect(memDC, &snakeRect, snakeBrush);
172 }
173
174 // Draw snake eyes
175 if (!snake.getBody().empty()) {
176 const Point& head = snake.getBody().front();
177 int eyeSize = 4; // 3 pixel radius for each eye
178 int eyeOffset = GRID_SIZE / 9; // 5->7 Offset from the edge of the head
179
180 // Calculate eye positions based on snake's direction
181 int leftEyeX, leftEyeY, rightEyeX, rightEyeY;
182 switch (snake.getCurrentDirection()) {
183 case UP:
184 leftEyeX = head.x * GRID_SIZE + eyeOffset;
185 rightEyeX = (head.x + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
186 leftEyeY = rightEyeY = head.y * GRID_SIZE + eyeOffset;
187 break;
188 case DOWN:
189 leftEyeX = head.x * GRID_SIZE + eyeOffset;
190 rightEyeX = (head.x + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
191 leftEyeY = rightEyeY = (head.y + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
192 break;
193 case LEFT:
194 leftEyeX = rightEyeX = head.x * GRID_SIZE + eyeOffset;
195 leftEyeY = head.y * GRID_SIZE + eyeOffset;
196 rightEyeY = (head.y + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
197 break;
198 case RIGHT:
199 leftEyeX = rightEyeX = (head.x + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
200 leftEyeY = head.y * GRID_SIZE + eyeOffset;
201 rightEyeY = (head.y + 1) * GRID_SIZE - eyeOffset - eyeSize * 2;
202 break;
203 }
204
205 // Draw the eyes
206 HBRUSH whiteBrush = CreateSolidBrush(RGB(255, 255, 255)); // yellow 255, 255, 0 darkpurple 128, 0, 128 purple 157, 0, 255 white 255,255,255
207 SelectObject(memDC, whiteBrush);
208 Ellipse(memDC, leftEyeX, leftEyeY, leftEyeX + eyeSize * 2, leftEyeY + eyeSize * 2);
209 Ellipse(memDC, rightEyeX, rightEyeY, rightEyeX + eyeSize * 2, rightEyeY + eyeSize * 2);
210 DeleteObject(whiteBrush);
211 }
212
213 // Draw food
214 if (food.x >= 0 && food.y >= 0) {
215 RECT foodRect = {
216 food.x * GRID_SIZE,
217 food.y * GRID_SIZE,
218 (food.x + 1) * GRID_SIZE,
219 (food.y + 1) * GRID_SIZE
220 };
221 FillRect(memDC, &foodRect, foodBrush);
222 }
223
224 WCHAR scoreText[32];
225 swprintf_s(scoreText, L"Score: %d", score);
226 SetBkMode(memDC, TRANSPARENT);
227 SetTextColor(memDC, RGB(255, 255, 255));
228 TextOut(memDC, 10, 10, scoreText, wcslen(scoreText));
229
230 if (gameOver) {
231 const WCHAR* gameOverText = L"Game Over! Press any arrow key to restart.";
232 TextOut(memDC, WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2, gameOverText, wcslen(gameOverText));
233 }
234 else if (paused) {
235 const WCHAR* pausedText = L"Paused. Press any arrow key to start.";
236 TextOut(memDC, WINDOW_WIDTH / 2 - 120, WINDOW_HEIGHT / 2, pausedText, wcslen(pausedText));
237 }
238
239 BitBlt(hdc, 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT, memDC, 0, 0, SRCCOPY);
240 }
241
242 void togglePause() {
243 paused = !paused;
244 }
245
246 void reset() {
247 snake = Snake();
248 do {
249 spawnFood();
250 } while (isSnakeOnPoint(food));
251 score = 0;
252 gameOver = false;
253 paused = true;
254 }
255
256 void handleKeyPress(WPARAM wParam) {
257 switch (wParam) {
258 case VK_UP:
259 case VK_DOWN:
260 case VK_LEFT:
261 case VK_RIGHT:
262 if (gameOver) {
263 reset();
264 }
265 else {
266 Direction newDir;
267 switch (wParam) {
268 case VK_UP: newDir = UP; break;
269 case VK_DOWN: newDir = DOWN; break;
270 case VK_LEFT: newDir = LEFT; break;
271 case VK_RIGHT: newDir = RIGHT; break;
272 }
273 snake.setDirection(newDir);
274 if (paused) paused = false;
275 }
276 break;
277 case VK_SPACE:
278 togglePause();
279 break;
280 case VK_ESCAPE:
281 reset();
282 break;
283 case VK_F1: // Add this case for F1 key
284 MessageBoxW(hwnd, L"Snake Game 1.3 Programmed in C++ Win32 API (383 lines of code) by Entisoft Software (c) Evans Thorpemorton", L"About", MB_OK | MB_ICONINFORMATION);
285 break;
286 }
287 }
288
289 static void AdjustWindowSize(HWND hwnd) {
290 RECT rcClient, rcWindow;
291 GetClientRect(hwnd, &rcClient);
292 GetWindowRect(hwnd, &rcWindow);
293 int width = (rcWindow.right - rcWindow.left) - (rcClient.right - rcClient.left) + WINDOW_WIDTH;
294 int height = (rcWindow.bottom - rcWindow.top) - (rcClient.bottom - rcClient.top) + WINDOW_HEIGHT;
295 SetWindowPos(hwnd, NULL, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER);
296 }
297}; // Add this closing brace to end the Game class
298
299Game* game = nullptr;
300
301LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
302 switch (uMsg) {
303 case WM_CREATE:
304 game = new Game(hwnd);
305 SetTimer(hwnd, 1, MOVE_INTERVAL, nullptr);
306 return 0;
307
308 case WM_DESTROY:
309 KillTimer(hwnd, 1);
310 delete game;
311 PostQuitMessage(0);
312 return 0;
313
314 case WM_TIMER:
315 game->update();
316 InvalidateRect(hwnd, nullptr, FALSE);
317 return 0;
318
319 case WM_PAINT:
320 {
321 PAINTSTRUCT ps;
322 HDC hdc = BeginPaint(hwnd, &ps);
323 game->render();
324 EndPaint(hwnd, &ps);
325 }
326 return 0;
327
328 case WM_KEYDOWN:
329 game->handleKeyPress(wParam);
330 return 0;
331 }
332
333 return DefWindowProc(hwnd, uMsg, wParam, lParam);
334}
335
336int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
337 const wchar_t CLASS_NAME[] = L"SnakeGameWindow";
338
339 WNDCLASS wc = {};
340 wc.lpfnWndProc = WindowProc;
341 wc.hInstance = hInstance;
342 wc.lpszClassName = CLASS_NAME;
343 wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
344
345 RegisterClass(&wc);
346
347 // Get the screen dimensions
348 int screenWidth = GetSystemMetrics(SM_CXSCREEN);
349 int screenHeight = GetSystemMetrics(SM_CYSCREEN);
350
351 // Calculate the window position to center it
352 int windowWidth = WINDOW_WIDTH + 16;
353 int windowHeight = WINDOW_HEIGHT + 39;
354 int posX = (screenWidth - windowWidth) / 2;
355 int posY = (screenHeight - windowHeight) / 2;
356
357 HWND hwnd = CreateWindowEx(
358 0,
359 CLASS_NAME,
360 L"Snake Game (ArrowKeys=Move Space=Pause Escape=Reset)",
361 WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
362 posX, posY, WINDOW_WIDTH + 16, WINDOW_HEIGHT + 39,
363 nullptr,
364 nullptr,
365 hInstance,
366 nullptr
367 );
368
369 if (hwnd == nullptr) {
370 return 0;
371 }
372
373 Game::AdjustWindowSize(hwnd);
374
375 ShowWindow(hwnd, nCmdShow);
376
377 MSG msg = {};
378 while (GetMessage(&msg, nullptr, 0, 0)) {
379 TranslateMessage(&msg);
380 DispatchMessage(&msg);
381 }
382
383 return 0;
384}
385