Skip to content

Instantly share code, notes, and snippets.

@MORTAL2000
Last active March 30, 2017 15:08
Show Gist options
  • Save MORTAL2000/b144b7d8e75333d704f3482e1fa8797b to your computer and use it in GitHub Desktop.
Save MORTAL2000/b144b7d8e75333d704f3482e1fa8797b to your computer and use it in GitHub Desktop.
#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