#include <windows.h>
#include <tchar.h>
#include <stdlib.h>
#include <vector>
#define SN_HELP _T("Управление игрой:\nКлавиша влево\nКлавиша вправо\nКлавиша вверх\nКлавиша вниз")
#define SN_TIMER 1000
#define SN_CLASS _T("SNAKE")
#define SN_APP _T("Змейка")
#define SN_WIDTH 640
#define SN_HEIGHT 480
#define SN_SIZE 16
#define SN_NONE 0
#define SN_BODY 1
#define SN_FOOD 2
#define SN_HEAD 3
#define COLS 40
#define ROWS 30
int create_window(HINSTANCE hinst, LPCTSTR cap);
void draw(HDC mdc, int dir, HBRUSH back, HPEN pen);
void draw_text(HDC mdc);
void start(int& dir);
void pos_food(void);
BYTE g_mat[ROWS][COLS];
bool g_over;
struct point {
int x, y;
point(void){}
point(int _x, int _y):x(_x), y(_y){}
};
std::vector<point> g_snake;
int WINAPI _tWinMain(HINSTANCE hinst, HINSTANCE, LPTSTR, int){
return create_window(hinst, SN_APP);
}
//старт
void start(int& dir){
dir = VK_UP;
g_snake.clear();
for(int i = 0; i < ROWS; ++i){
for(int j = 0; j < COLS; ++j)
g_mat[i][j] = SN_NONE;
}
for(int b = 0; b < 3; ++b){
g_snake.push_back(point(COLS/2, ROWS/2 + b));
const point& p = g_snake.back();
g_mat[p.y][p.x] = SN_BODY;
}
pos_food();
g_over = false;
}
//вывод
void draw(HDC mdc, int dir, HBRUSH back, HPEN pen, HBRUSH food){
if(g_over){
draw_text(mdc);
return;
}
point prev = g_snake.front();
switch(dir){
case VK_LEFT:
--g_snake.front().x;
break;
case VK_RIGHT:
++g_snake.front().x;
break;
case VK_UP:
--g_snake.front().y;
break;
case VK_DOWN:
++g_snake.front().y;
break;
}
//проверка на коллизию
point q = g_snake.front();
if((q.x < 0) || (q.y < 0) || (q.y >= ROWS) || (q.x >= COLS) || (g_mat[q.y][q.x] == SN_BODY)){
g_over = true;
return;
}
point e = g_snake.back();
std::vector<point>::size_type s;
for(s = 1; s < g_snake.size(); ++s)
std::swap(g_snake[s], prev);
if(g_mat[q.y][q.x] == SN_FOOD){
if(g_snake.size() > 512){
g_over = true;
return;
}
g_snake.push_back(e);
g_mat[q.y][q.x] = SN_NONE;
pos_food();
}
g_mat[e.y][e.x] = SN_NONE;
g_mat[g_snake.front().y][g_snake.front().x] = SN_HEAD;
for(s = 1; s < g_snake.size(); ++s)
g_mat[g_snake[s].y][g_snake[s].x] = SN_BODY;
//рисование змейки, пищи
int x, y, m;
RECT rc;
HGDIOBJ a, b;
a = SelectObject(mdc, back);
b = SelectObject(mdc, pen);
for(int i = 0; i < ROWS; ++i){
for(int j = 0; j < COLS; ++j){
switch(g_mat[i][j]){
case SN_HEAD: //вывод головы
x = j * SN_SIZE;
y = i * SN_SIZE;
m = SetROP2(mdc, R2_XORPEN);
Ellipse(mdc, x, y, x + SN_SIZE, y + SN_SIZE);
SetROP2(mdc, m);
break;
case SN_BODY: //вывод тела
x = j * SN_SIZE;
y = i * SN_SIZE;
RoundRect(mdc, x, y, x + SN_SIZE, y + SN_SIZE, 14, 14);
break;
case SN_FOOD: //вывод пищи
x = j * SN_SIZE;
y = i * SN_SIZE;
SetRect(&rc, x, y, x + SN_SIZE, y + SN_SIZE);
FillRect(mdc, &rc, food);
break;
}
}
}
SelectObject(mdc, a);
SelectObject(mdc, b);
}
//расстановка пищи
void pos_food(void){
int x, y;
do {
x = rand() % COLS;
y = rand() % ROWS;
if(g_mat[y][x] == SN_NONE){
g_mat[y][x] = SN_FOOD;
break;
}
} while(1);
}
//вывод текста
void draw_text(HDC mdc){
const TCHAR s1[]= _T("КОНЕЦ ИГРЫ");
const int n1 = sizeof(s1)/sizeof(s1[0])-1;
SIZE sz;
GetTextExtentPoint32(mdc, s1, n1, &sz);
TextOut(mdc, (SN_WIDTH - sz.cx)/2, (SN_HEIGHT - sz.cy)/2 - sz.cy*2, s1, n1);
const TCHAR s2[]= _T("НАЧАТЬ ЗАНОВО КЛАВИША ENTER");
const int n2 = sizeof(s2)/sizeof(s2[0])-1;
GetTextExtentPoint32(mdc, s2, n2, &sz);
TextOut(mdc, (SN_WIDTH - sz.cx)/2, (SN_HEIGHT - sz.cy)/2 + sz.cy, s2, n2);
}
//обработчик
LRESULT CALLBACK wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
static HBITMAP hbm = NULL;
static HBRUSH back = NULL;
static HPEN pen = NULL;
static HBRUSH food = NULL;
static HDC mdc = NULL;
static int dir = VK_UP;
HDC hdc;
switch(msg) {
case WM_CREATE:
hdc = GetDC(hwnd);
mdc = CreateCompatibleDC(hdc);
hbm = CreateCompatibleBitmap(hdc, SN_WIDTH, SN_HEIGHT);
SelectObject(mdc, hbm);
ReleaseDC(hwnd, hdc);
back = CreateSolidBrush(RGB(0x55, 0xFF, 0x55));
pen = CreatePen(PS_SOLID, 1, RGB(0, 0xAA, 0));
food = CreateSolidBrush(RGB(0xFF, 0x88, 0x35));
start(dir);
MessageBox(hwnd, SN_HELP, SN_APP, MB_OK | MB_ICONQUESTION);
SetTimer(hwnd, SN_TIMER, 95, NULL);
break;
case WM_ERASEBKGND:
PatBlt(mdc, 0, 0, SN_WIDTH, SN_HEIGHT, WHITENESS);
draw(mdc, dir, back, pen, food);
BitBlt((HDC)wParam, 0, 0, SN_WIDTH, SN_HEIGHT, mdc, 0, 0, SRCCOPY);
return 1;
case WM_KEYDOWN:
switch(LOWORD(wParam)){
case VK_LEFT:
dir = VK_LEFT;
break;
case VK_RIGHT:
dir = VK_RIGHT;
break;
case VK_UP:
dir = VK_UP;
break;
case VK_DOWN:
dir = VK_DOWN;
break;
case VK_RETURN:
if(g_over)
start(dir);
break;
}
break;
case WM_TIMER:
InvalidateRect(hwnd, NULL, TRUE);
break;
case WM_DESTROY:
DeleteDC(mdc);
DeleteObject(hbm);
DeleteObject(back);
DeleteObject(pen);
DeleteObject(food);
PostQuitMessage(0);
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
//создание окна
int create_window(HINSTANCE hinst, LPCTSTR cap){
WNDCLASSEX cls = {0};
cls.cbSize = sizeof(cls);
cls.lpfnWndProc = (WNDPROC)wnd_proc;
cls.hInstance = hinst;
cls.hCursor = LoadCursor(NULL, IDC_ARROW);
cls.lpszClassName = SN_CLASS;
if(!RegisterClassEx(&cls))
return 1;
DWORD sty = WS_OVERLAPPEDWINDOW & ~(WS_MAXIMIZEBOX | WS_SIZEBOX);
RECT rc = { 0, 0, SN_WIDTH, SN_HEIGHT };
AdjustWindowRectEx(&rc, sty, FALSE, 0);
int width = rc.right - rc.left;
int height= rc.bottom - rc.top;
HWND hwnd = CreateWindowEx(0, SN_CLASS, cap, sty, (GetSystemMetrics(SM_CXSCREEN) - width)/2,
(GetSystemMetrics(SM_CYSCREEN) - height)/2, width, height, NULL, NULL, hinst, NULL);
if(hwnd == NULL){
UnregisterClass(SN_CLASS, hinst);
return 1;
}
ShowWindow(hwnd, SW_SHOW);
UpdateWindow(hwnd);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
UnregisterClass(SN_CLASS, hinst);
return 0;
}