Last active
March 30, 2017 15:08
-
-
Save MORTAL2000/b144b7d8e75333d704f3482e1fa8797b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <SFML/Window.hpp> | |
#include <iostream> | |
#include <random> | |
#include <array> | |
#include <gl/glut.h> | |
enum { MINE = 9 }; | |
enum { TILE_SIZE = 20 }; | |
enum { MARGIN = 40 }; | |
enum { PADDING = 10 }; | |
enum { BOARD_SIZE = 9 }; | |
enum { MINE_COUNT = 10 }; | |
enum Color { | |
RED, | |
DARKRED, | |
BLUE, | |
DARKBLUE, | |
GREEN, | |
DARKGREEN, | |
CYAN, | |
DARKCYAN, | |
YELLOW, | |
DARKYELLOW, | |
WHITE, | |
MAGENTA, | |
BLACK, | |
DARKGRAY, | |
LIGHTGRAY, | |
ULTRALIGHTGRAY | |
}; | |
static const struct | |
{ | |
float r, g, b; | |
} colors[] = | |
{ | |
{ 1, 0, 0 },// red | |
{ 0.5f, 0, 0 },// dark red | |
{ 0, 0, 1 }, // blue | |
{ 0, 0, 0.5f }, // dark blue | |
{ 0, 1, 0 }, // green | |
{ 0, 0.5f, 0 }, // dark green | |
{ 0, 1, 1 }, // cyan | |
{ 0, 0.5f, 0.5f }, // dark cyan | |
{ 1, 1, 0 },//yellow | |
{ 0.5f, 0.5f, 0 },//dark yellow | |
{ 1, 1, 1 },// White | |
{ 1, 0, 1 }, // magenta | |
{ 0, 0, 0 }, // black | |
{ 0.25, 0.25, 0.25 }, // dark gray | |
{ 0.5, 0.5, 0.5 }, // light gray | |
{ 0.75, 0.75, 0.75 }, // ultra-light gray | |
}; | |
struct cell | |
{ | |
int type = 0; | |
bool flag = false; | |
bool open = false; | |
}; | |
static std::array<cell, BOARD_SIZE*BOARD_SIZE> board; | |
static constexpr std::array<int, 3> directions{ -1, 0, +1 }; | |
int explode = -1; | |
int width; | |
int height; | |
bool clicked = false; | |
sf::Clock game_clock; | |
int num_opened; | |
int rand_int(int low, int high) | |
{ | |
static std::default_random_engine re{ std::random_device{}() }; | |
using Dist = std::uniform_int_distribution<int>; | |
static Dist uid{}; | |
return uid(re, Dist::param_type{ low,high }); | |
} | |
void drawRect(int x, int y, float width, float height, const Color& color = LIGHTGRAY, bool outline = true) | |
{ | |
glColor3f(colors[color].r, colors[color].g, colors[color].b); | |
glBegin(outline ? GL_LINE_STRIP : GL_TRIANGLE_FAN); | |
{ | |
glVertex2i(x + 0 * width, y + 0 * height); | |
glVertex2i(x + 1 * width, y + 0 * height); | |
glVertex2i(x + 1 * width, y + 1 * height); | |
glVertex2i(x + 0 * width, y + 1 * height); | |
} | |
glEnd(); | |
} | |
void drawCircle(int cx, int cy, float radius, const Color& color = LIGHTGRAY, bool outline = true) | |
{ | |
glColor3f(colors[color].r, colors[color].g, colors[color].b); | |
glBegin(outline ? GL_LINE_LOOP : GL_TRIANGLE_FAN); | |
for (int i = 0; i <= 32; i++) { | |
float angle = 2 * 3.14159 * i / 32.0f; | |
float x = radius * cosf(angle); | |
float y = radius * sinf(angle); | |
glVertex2f(x + cx, y + cy); | |
} | |
glEnd(); | |
} | |
void drawFlag(int x, int y) | |
{ | |
glColor3f(colors[BLACK].r, colors[BLACK].g, colors[BLACK].b); | |
x = (x*TILE_SIZE) + PADDING + 6; | |
y = (y*TILE_SIZE) + PADDING + 3; | |
//platform | |
glBegin(GL_POLYGON); | |
{ | |
glVertex2i(x + 0, y + 2); | |
glVertex2i(x + 9, y + 2); | |
glVertex2i(x + 9, y + 3); | |
glVertex2i(x + 7, y + 3); | |
glVertex2i(x + 7, y + 4); | |
glVertex2i(x + 3, y + 4); | |
glVertex2i(x + 3, y + 3); | |
glVertex2i(x + 0, y + 3); | |
} | |
glEnd(); | |
//mast | |
glBegin(GL_LINES); | |
{ | |
glVertex2i(x + 4, y + 4); | |
glVertex2i(x + 4, y + 7); | |
} | |
glEnd(); | |
//flag | |
glColor3f(colors[RED].r, colors[RED].g, colors[RED].b); | |
glBegin(GL_TRIANGLES); | |
{ | |
glVertex2i(x + 5, y + 7); | |
glVertex2i(x + 5, y + 12); | |
glVertex2i(x + 0, y + 9); | |
} | |
glEnd(); | |
} | |
void drawMine(int x, int y, bool dead) | |
{ | |
if (dead) | |
{ | |
drawRect(x*TILE_SIZE + PADDING, y*TILE_SIZE + PADDING, TILE_SIZE, TILE_SIZE, RED, false); | |
} | |
x = (x*TILE_SIZE) + PADDING + 4; | |
y = (y*TILE_SIZE) + PADDING + 4; | |
//spikes | |
glColor3f(colors[BLACK].r, colors[BLACK].g, colors[BLACK].b); | |
glBegin(GL_LINES); | |
{ | |
glVertex2i(x + 5, y - 1); | |
glVertex2i(x + 5, y + 12); | |
glVertex2i(x - 1, y + 5); | |
glVertex2i(x + 12, y + 5); | |
glVertex2i(x + 1, y + 1); | |
glVertex2i(x + 10, y + 10); | |
glVertex2i(x + 1, y + 10); | |
glVertex2i(x + 10, y + 1); | |
} | |
glEnd(); | |
//ball | |
glBegin(GL_POLYGON); | |
{ | |
glVertex2i(x + 3, y + 1); | |
glVertex2i(x + 1, y + 4); | |
glVertex2i(x + 1, y + 7); | |
glVertex2i(x + 3, y + 10); | |
glVertex2i(x + 8, y + 10); | |
glVertex2i(x + 10, y + 7); | |
glVertex2i(x + 10, y + 4); | |
glVertex2i(x + 8, y + 1); | |
} | |
glEnd(); | |
//shine | |
drawRect(x + 3, y + 5, 2, 2, WHITE, false); | |
} | |
void drawNum(int x, int y, int v) | |
{ | |
switch (v) | |
{ | |
case 1: | |
glColor3f(colors[BLUE].r, colors[BLUE].g, colors[BLUE].b); | |
break; | |
case 2: | |
glColor3f(colors[GREEN].r, colors[GREEN].g, colors[GREEN].b); | |
break; | |
case 3: | |
glColor3f(colors[RED].r, colors[RED].g, colors[RED].b); | |
break; | |
case 4: | |
glColor3f(colors[DARKBLUE].r, colors[DARKBLUE].g, colors[DARKBLUE].b); | |
break; | |
case 5: | |
glColor3f(colors[DARKRED].r, colors[DARKRED].g, colors[DARKRED].b); | |
break; | |
case 6: | |
glColor3f(colors[DARKYELLOW].r, colors[DARKYELLOW].g, colors[DARKYELLOW].b); | |
break; | |
case 7: | |
glColor3f(colors[CYAN].r, colors[CYAN].g, colors[CYAN].b); | |
break; | |
case 8: | |
glColor3f(colors[DARKCYAN].r, colors[DARKCYAN].g, colors[DARKCYAN].b); | |
break; | |
} | |
glRasterPos2i((x + 0)*TILE_SIZE + PADDING + 6, (y + 0)*TILE_SIZE + PADDING + 5); | |
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, '0' + v); | |
} | |
void drawFrame(float x, float y, float width, float height, bool doubleFrame = true) | |
{ | |
glColor3f(colors[WHITE].r, colors[WHITE].g, colors[WHITE].b); | |
glBegin(GL_LINE_LOOP); | |
{ | |
glVertex2f((x + 0) + 0 * width, (y - 0) + 0 * height); | |
glVertex2f((x - 0) + 0 * width, (y - 1) + 1 * height); | |
glVertex2f((x - 1) + 1 * width, (y - 1) + 1 * height); | |
glVertex2f((x - 2) + 1 * width, (y - 2) + 1 * height); | |
glVertex2f((x + 1) + 0 * width, (y - 2) + 1 * height); | |
glVertex2f((x + 1) + 0 * width, (y + 1) + 0 * height); | |
} | |
glEnd(); | |
glColor3f(colors[LIGHTGRAY].r, colors[LIGHTGRAY].g, colors[LIGHTGRAY].b); | |
glBegin(GL_LINE_LOOP); | |
{ | |
glVertex2f((x - 2) + 1 * width, (y - 2) + 1 * height); | |
glVertex2f((x - 2) + 1 * width, (y + 1) + 0 * height); | |
glVertex2f((x + 1) + 0 * width, (y + 1) + 0 * height); | |
glVertex2f((x - 0) + 0 * width, (y - 0) + 0 * height); | |
glVertex2f((x - 1) + 1 * width, (y - 0) + 0 * height); | |
glVertex2f((x - 1) + 1 * width, (y - 1) + 1 * height); | |
} | |
glEnd(); | |
if (!doubleFrame) return; | |
width = width - 2 * PADDING; | |
height = height - 2 * PADDING; | |
glBegin(GL_LINE_LOOP); | |
{ | |
glVertex2f((x - 0 + PADDING) + 0 * width, (y + PADDING - 0) + 0 * height); | |
glVertex2f((x - 0 + PADDING) + 0 * width, (y + PADDING - 1) + 1 * height); | |
glVertex2f((x - 1 + PADDING) + 1 * width, (y + PADDING - 1) + 1 * height); | |
glVertex2f((x - 2 + PADDING) + 1 * width, (y + PADDING - 2) + 1 * height); | |
glVertex2f((x + 1 + PADDING) + 0 * width, (y + PADDING - 2) + 1 * height); | |
glVertex2f((x + 1 + PADDING) + 0 * width, (y + PADDING + 1) + 0 * height); | |
} | |
glEnd(); | |
glColor3f(colors[WHITE].r, colors[WHITE].g, colors[WHITE].b); | |
glBegin(GL_LINE_LOOP); | |
{ | |
glVertex2i((x + PADDING - 2) + 1 * width, (y + PADDING - 2) + 1 * height); | |
glVertex2i((x + PADDING - 2) + 1 * width, (y + PADDING + 1) + 0 * height); | |
glVertex2i((x + PADDING + 1) + 0 * width, (y + PADDING + 1) + 0 * height); | |
glVertex2i((x + PADDING - 0) + 0 * width, (y + PADDING - 0) + 0 * height); | |
glVertex2i((x + PADDING - 1) + 1 * width, (y + PADDING - 0) + 0 * height); | |
glVertex2i((x + PADDING - 1) + 1 * width, (y + PADDING - 1) + 1 * height); | |
} | |
glEnd(); | |
} | |
void drawClosedDim(int x, int y) | |
{ | |
drawFrame(x *TILE_SIZE + PADDING, y*TILE_SIZE + PADDING, TILE_SIZE, TILE_SIZE, false); | |
} | |
void drawOpenDim(int x, int y) | |
{ | |
drawRect(x*TILE_SIZE + PADDING, y*TILE_SIZE + PADDING, TILE_SIZE, TILE_SIZE); | |
} | |
void drawUpperFrame(int x = 0, int y = 0) | |
{ | |
static const float upper_frame_outter_width = width; | |
static const float upper_frame_outter_height = 2 * MARGIN; | |
static const float offset = height - upper_frame_outter_height; | |
drawFrame(0, offset, upper_frame_outter_width, upper_frame_outter_height); | |
} | |
void drawLowerFrame(int x = 0, int y = 0) | |
{ | |
static const float lower_frame_outter_size = width; | |
drawFrame(0, 0, lower_frame_outter_size, lower_frame_outter_size); | |
} | |
void drawIcon(int x = 0, int y = 0) | |
{ | |
static const float icon_size = 2 * TILE_SIZE; | |
if (clicked) | |
{ | |
int x = 0, y = 0; | |
static const float cx = (width - icon_size) / 2.0f; | |
static const float cy = (height - MARGIN) - icon_size / 2.0f; | |
drawRect(cx, cy, 2 * TILE_SIZE, 2 * TILE_SIZE, ULTRALIGHTGRAY, false); | |
if (game_clock.getElapsedTime().asSeconds() > 0.25f) { | |
clicked = false; | |
game_clock.restart(); | |
} | |
} | |
drawFrame((width - icon_size) / 2.0f, (height - MARGIN) - icon_size / 2.0f, icon_size, icon_size, false); | |
static const float cx = width / 2.0f; | |
static const float cy = (height - MARGIN); | |
// face | |
drawCircle(x + cx, y + cy, TILE_SIZE*0.707f, YELLOW, false); | |
drawCircle(x + cx, y + cy, TILE_SIZE*0.707f, DARKGRAY); | |
// eyes | |
glBegin(GL_POINTS); | |
glVertex2f(-4.707 + cx, 1.707 + cy); | |
glVertex2f(4.707 + cx, 1.707 + cy); | |
glEnd(); | |
// mouth | |
glBegin(GL_LINES); | |
{ | |
glVertex2f(-3.707 + cx, -8.707 + cy); | |
glVertex2f(3.707 + cx, -8.707 + cy); | |
} | |
glEnd(); | |
} | |
int index(int x, int y) | |
{ | |
return x + (y*BOARD_SIZE); | |
} | |
bool isOpen(int x, int y) | |
{ | |
return board[index(x, y)].open; | |
} | |
int getType(int x, int y) | |
{ | |
return board[index(x, y)].type; | |
} | |
void setType(int x, int y, int v) | |
{ | |
board[index(x, y)].type = v; | |
} | |
bool isMine(int x, int y) | |
{ | |
if (x < 0 || y < 0 || x > BOARD_SIZE - 1 || y > BOARD_SIZE - 1) | |
return false; | |
if (getType(x, y) == MINE) | |
return true; | |
return false; | |
} | |
int calcMine(int x, int y) | |
{ | |
int mines = 0; | |
for (const auto i : directions) { | |
for (const auto j : directions) { | |
mines += isMine(x + i, y + j); | |
} | |
} | |
return mines; | |
} | |
bool isFlag(int x, int y) | |
{ | |
return board[index(x, y)].flag; | |
} | |
bool gameOver() | |
{ | |
return explode != -1; | |
} | |
bool hasWon() | |
{ | |
return num_opened == MINE_COUNT; | |
} | |
bool isExploded(int x, int y) | |
{ | |
return explode == index(x, y); | |
} | |
void openMines(bool open = true) | |
{ | |
for (int y = 0; y < BOARD_SIZE; y++) { | |
for (int x = 0; x < BOARD_SIZE; x++) { | |
if (isMine(x, y)) | |
board[index(x, y)].open = open; | |
} | |
} | |
} | |
bool requestRestart(int x, int y) | |
{ | |
return (x >= 3 && x <= 5 && y >= 10 && y <= 12); | |
} | |
void init() | |
{ | |
game_clock.restart(); | |
for (int i = 0; i < BOARD_SIZE*BOARD_SIZE; i++) { | |
board[i].type = 0; | |
board[i].flag = false; | |
board[i].open = false; | |
} | |
for (int i = 0; i<MINE_COUNT; i++) | |
{ | |
bool tmp = true; | |
do | |
{ | |
int x = rand_int(0, BOARD_SIZE - 1); | |
int y = rand_int(0, BOARD_SIZE - 1); | |
if (!isMine(x, y)) | |
{ | |
tmp = false; | |
setType(x, y, MINE); | |
} | |
} while (tmp); | |
} | |
//compute | |
for (int y = 0; y < BOARD_SIZE; y++) { | |
for (int x = 0; x < BOARD_SIZE; x++) { | |
if (!isMine(x, y)) { | |
setType(x, y, calcMine(x, y)); | |
} | |
} | |
} | |
explode = -1; | |
clicked = true; | |
num_opened = BOARD_SIZE*BOARD_SIZE; | |
} | |
void openCell(int x, int y) | |
{ | |
if (x < 0 || y < 0 || y > BOARD_SIZE - 1 || x > BOARD_SIZE - 1) | |
return; | |
if (isOpen(x, y)) | |
return; | |
num_opened--; | |
board[index(x, y)].open = true; | |
if (isMine(x, y)) | |
{ | |
explode = index(x, y); | |
openMines(); | |
return; | |
} | |
if (getType(x, y) == 0) | |
{ | |
for (const auto i : directions) { | |
for (const auto j : directions) { | |
openCell(x + i, y + j); | |
} | |
} | |
} | |
} | |
void toggleFlag(int x, int y) | |
{ | |
board[index(x, y)].flag = !isFlag(x, y); | |
} | |
void drawOpen(int x, int y, int n, bool dead) | |
{ | |
switch (n) { | |
case 0: | |
drawOpenDim(x, y); | |
break; | |
case 9: | |
if (!dead) { | |
drawOpenDim(x, y); | |
} | |
drawMine(x, y, dead); | |
break; | |
default: | |
drawOpenDim(x, y); | |
drawNum(x, y, n); | |
} | |
} | |
void drawClosed(int x, int y) | |
{ | |
drawClosedDim(x, y); | |
if (isFlag(x, y)) | |
drawFlag(x, y); | |
} | |
void draw() | |
{ | |
for (int y = 0; y < BOARD_SIZE; y++) | |
{ | |
for (int x = 0; x < BOARD_SIZE; x++) | |
{ | |
if (isOpen(x, y)) | |
drawOpen(x, y, getType(x, y), isExploded(x, y)); | |
else | |
drawClosed(x, y); | |
} | |
} | |
if (gameOver() || hasWon()) { | |
if (game_clock.getElapsedTime().asSeconds() > 0.25f) { | |
static int toggle = 1; | |
toggle ^= 1; | |
openMines(toggle == 0); | |
game_clock.restart(); | |
} | |
} | |
} | |
int main() | |
{ | |
width = BOARD_SIZE*TILE_SIZE + 2 * PADDING; | |
height = BOARD_SIZE*TILE_SIZE + 2 * PADDING + 2 * MARGIN; | |
sf::Window window(sf::VideoMode(width, height), "", sf::Style::Close); | |
window.setVerticalSyncEnabled(true); | |
glClearColor(0.8f, 0.8f, 0.8f, 1.f); | |
glMatrixMode(GL_PROJECTION); | |
glLoadIdentity(); | |
glOrtho(0, width, 0, height, -1.f, 1.f); | |
glPointSize(5.0); | |
glEnable(GL_LINE_SMOOTH); | |
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); | |
glEnable(GL_POINT_SMOOTH); | |
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
init(); | |
bool running = true; | |
while (running) | |
{ | |
sf::Event event; | |
while (window.pollEvent(event)) | |
{ | |
if (event.type == sf::Event::Closed) | |
{ | |
running = false; | |
} | |
else if (event.type == sf::Event::Resized) | |
{ | |
glViewport(0, 0, event.size.width, event.size.height); | |
} | |
else if (event.type == sf::Event::KeyPressed) { | |
if (event.key.code == sf::Keyboard::Escape) { | |
running = false; | |
} | |
} | |
else if (event.type == sf::Event::MouseButtonPressed) { | |
int x = (event.mouseButton.x + PADDING) / TILE_SIZE - 1; | |
int y = (height - event.mouseButton.y + PADDING) / TILE_SIZE - 1; | |
if (event.mouseButton.button == sf::Mouse::Left) { | |
if (requestRestart(x, y)) { | |
init(); | |
} | |
else if (!gameOver()) { | |
openCell(x, y); | |
} | |
} | |
if (event.mouseButton.button == sf::Mouse::Right) { | |
if (gameOver()) break; | |
toggleFlag(x, y); | |
} | |
} | |
} | |
glClear(GL_COLOR_BUFFER_BIT); | |
drawLowerFrame(); | |
drawUpperFrame(); | |
drawIcon(); | |
draw(); | |
window.display(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment