Сделать игру сапер (minesweeper)
Нужна помощь с написанием кода для игры сапер. Надо сделать экран где пользователь будет видеть пустые ячейки или квадратики как в игре сапер. И программа должна потребовать ввести инпут координаты. Например если квадратики 10*10, то координаты ввести как 5,6 и расставить мины рандомно. Если в введенной координатами точке будет мина то игра останавливается и программа запросит о продолжении игры у человека. А если человек не попадает на мину и открывает все пустые ячейки то он выигрывает.
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
Ответы с готовыми решениями:
Создать игру сапер
Создать игру — консольное приложение, популярный аналог игры "Сапер". Игровое поле имеет.
Написать игру «Сапёр» использую указанные классы
Задача следующая: нужно написать сапёр, использую при этом классы: cell (абстрактный класс), его.
задача про игру «сапер»
Мой алгоритм: #include <iostream> #include <time.h> #include <windows.h> using namespace std;.
Сделать игру «Сапёр». Использовать кнопку «datagridview»
Помогите написать код.Нужно сделать игру "САПЁР" матрица 10×10.Использовать кнопку.
263 / 182 / 87
Регистрация: 03.05.2020
Сообщений: 790
esepwi, и на чем собираетесь делать?
Регистрация: 22.09.2020
Сообщений: 4
простите, забыл упомянуть. C++
Добавлено через 1 минуту
C++
263 / 182 / 87
Регистрация: 03.05.2020
Сообщений: 790
консольное?
Регистрация: 22.09.2020
Сообщений: 4
да , не обязательно графически дизайн делать
525 / 487 / 98
Регистрация: 25.12.2011
Сообщений: 1,176
Помощь, это не писать за вас код.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#include #include using namespace std; void update(int* field, int size) { system("cls"); for (int i = 1; i size; i++) { switch (field[i - 1]) { case 0: cout "□"; break; case 1: cout "■"; break; case 2: cout "□"; break; } if (!(i % 10)) cout endl; } } int* init(int size, int count_bomb) { int* field = new int[sizeof(int) * size]; for (int i = 0; i size; i++) { field[i] = 0; } srand(time(0)); for (int i = 0; i count_bomb; i++) field[rand() % 100] = 2; //Можешь использовать random_shuffle() или массив, т.к. rand() может повторяться. return field; } bool input(int* field, int len_line) { int x, y; cout "Enter X and Y: "; cin >> x >> y; x--; y--; if (field[y * len_line + x] == 2) return false; else { field[y * len_line + x] = 1; return true; } } int main() { int x = 10, y = 10; int b = 10, win = 0; int size = x * y; int* field = init(size, b); update(field, size); while (true) { if (input(field, x)) { update(field, size); if (++win >= size - b) cout "You Win!" endl; } else { cout "You Lose! Score: " win endl; break; } } }
Как это сделано: пишем «Сапера» за 4 минуты
От переводчика: этот пост — перевод оригинальной статьи Маки Чиза, опытного кодера, который не только пишет классные программы, но и демонстрирует возможности различных языков своим коллегам, как новичкам, так и профессионалам.
«Сапер» — веселая игра, многие из нас в нее играют. Может быть, вы хотите сделать «Сапера» сами?
Напоминаем: для всех читателей «Хабра» — скидка 10 000 рублей при записи на любой курс Skillbox по промокоду «Хабр».
Уникальность игры в том, что она очень простая и при этом весьма увлекательная. В «Сапере» нет никакой хитрой геймплейной механики, вы просто нажимаете на квадратики, надеясь, что под ними нет мины.
Предлагаю попробовать написать «Сапера» на Processing. Это отличный инструмент, который позволяет создавать графические приложения на Java. Ссылка на него вот здесь.
Прежде чем начать, скажу, что этот туториал рассчитан на тех, кто уже знает Java. Опыт работы с Processing необязателен.
Итак, начинаем. Первый шаг — определение состояния игры. Я решил реализовать это вот так.
int gridW; // grid width int gridH; // grid height int numMines; // number of mines on the board int[][] mines; // entry is 1 for having a mine and 0 for not boolean[][] flags; // entry is true if you have flagged that spot boolean[][] revealed; // entry is true if that spot is revealed
Здесь, кажется, имеет смысл все, кроме вот этого участка: int[][] mines. Почему сетка мин является целым числом, а не булевым? Дело в том, что это позволяет легко подсчитать, сколько мин находится рядом с определенной позицией.
int calcNear(int x, int y) < int i=0; for (int offsetX=-1; offsetX> return i; >
Этот код определяет, сколько мин находится рядом с определенным участком. После того, как мы уберем исключения, мы получим что-то похожее на это:
boolean outBounds(int x,int y)< return x<0||y<0||x>=gridW||y>=gridH; > int calcNear(int x, int y) < if(outBounds(x,y))return 0; int i=0; for (int offsetX=-1; offsetX> return i; >
Главная задача самой игры — раскрывать квадраты, начиная с точки х, у.
void reveal(int x, int y)
В итоге мы имеем следующий алгоритм:
- Если позиция за пределами поля, end;
- Если позиция раскрыта, end;
- Раскрываем текущую позицию;
- Если у нас бомба рядом с текущей позицией, end;
- Если мы дошли до этого пункта во время выполнения функции, текущая позиция в пределах игрового поля была обнаружена и рядом с ней нет бомб, открываем смежные квадраты.
Если вы хотите узнать о визуализации, читайте дальше.
Визуализация
Здесь все должно быть понятно, но я объясню сложные части.
Итак, у нас есть переменная cellSize, определяющая количество пикселей в каждом квадрате.
void settings()
Таким образом мы создаем поле со сторонами gridW x gridH, где размеры каждого квадрата будут равняться значению cellSize.
Затем мы возвращаем переменные в изначальное состояние.
void setup() < //initialize and clear the arrays mines=new int[gridW][gridH]; flags=new boolean[gridW][gridH]; revealed=new boolean[gridW][gridH]; for(int x=0;x> >
Для инициализации поля:
//Place numMines mines on the grid void placeMines() < int i=0; while(i> //Clear the mines void clearMines() < for (int x=0; x> >
И далее включаем реакцию на клики мышью.
//We don't want the first click to be a mine boolean firstClick=true; void mousePressed() < int x=int(mouseX/cellSize); int y=int(mouseY/cellSize); //Right-click is flagging or de-flagging a square if (mouseButton==RIGHT) < flags[x][y]=!flags[x][y]; return; >else/left-click //Avoid making the first click a mine if (firstClick) < firstClick=false; do < clearMines(); placeMines(); > while (calcNear(x,y)!=0); > //Check for game loss if (mines[x][y]!=0) < println("Dang!"); exit(); >else/If game not lost, reveal starting from that square reveal(x, y); > > >
И функция отображения, вызываем ее один раз для каждого кадра.
void draw() < background(0); //For each cell for (int x=0; xelse if (revealed[x][y]) < col1=color(255/2); col2=color(255/2); >if (near==1)txtColor=color(255*1/4, 255*1/4, 255*3/4); if (near==2)txtColor=color(255*1/4, 255*3/4, 155*1/4); if (near==3)txtColor=color(255, 0, 0); if (near==4)txtColor=color(0, 0, 255); if (near==5)txtColor=color(255, 0, 0); boolean alternate=(x+y)%2==0; if (alternate) < fill(col2); stroke(col2); >else < fill(col1); stroke(col1); >//if(mines[x][y]>0) < //fill(0,255,0); //stroke(0,255,0); //>rect(x*cellSize, y*cellSize, cellSize, cellSize); //If there is a mine near this spot and it is revealed if (near>0&&revealed[x][y]) < fill(txtColor); noStroke(); textAlign(LEFT, TOP); textSize(cellSize); text(""+near, x*cellSize, y*cellSize); >> > >
И все, вот и наш «Сапер».
Игра выглядит просто, но в целом она полностью функциональна. И помните: «Сапер» вызывает привыкание!
- Онлайн-курс «Профессия frontend-разработчик».
- Практический годовой курс PHP-разработчик.
- Практический курс «Мобильный разработчик PRO».
Реализация алгоритма Сапера
Наткнулся в интернете на реализацию сапера, но сразу понял, что работает он немного не так, как нужно. (А точнее открывает с одного клика почти все поле) Пытался сам исправить, но все тщетно. Вот сам код реализации открытие полей. . Вот так реализуется раставление мин
#define FIELD_X 19 #define FIELD_Y 19 int field[FIELD_X][FIELD_Y]; void putMines() //Рандомно раскладываем мины < for(int z=0;z>
Вот так реализуется прорисовка клеток
app.clear(sf::Color(255,255,255)); for(int yqwe = 1; yqwe case 2: case 3: case 4: case 5: case 6: case 9: case 0: case 10: > > else //Иначе рисуем флажок sprite.setPosition((zqwe-1)*30,(yqwe-1)*30); app.draw(sprite); > > app.draw(exit.button); app.draw(exit.text); app.draw(newGame.button); app.draw(newGame.text); app.draw(settings.button); app.draw(settings.text); if (settingsWindow) < app.draw(setWin.title); app.draw(setWin.leftBorder); app.draw(setWin.rightBorder); app.draw(setWin.botBorder); app.draw(setWin.body); >if (lose) < app.draw(loseWin.title); app.draw(loseWin.leftBorder); app.draw(loseWin.rightBorder); app.draw(loseWin.botBorder); app.draw(loseWin.body); >app.display();
Сапер на VBA
Однажды, мне захотелось попробовать сделать элементарную игру «Сапер» в Excel на VBA.
Для тех, кто не знает, расскажу правила игры: есть квадратное поле, которое состоит из ячеек, например 20х20. В этих ячейках расположено определенное количество мин (пусть будет 40). Все ячейки скрыты и игрок не знает, где эти мины спрятаны. Игрок пошагово открывает ячейку за ячейкой, пытаясь не попасть на мину. Когда игрок нажимает на пустую ячейку, она открывается и открывается некоторая область пустых ячеек вокруг нее. Данная пустая область ограничена ячейками, в которой содержатся цифры — цифра в ячейке показывает сколько мин расположено вокруг нее:
Игра завершается тогда, когда открыты все ячейки, кроме тех, в которых содержатся мины — то есть в нашем случае (20х20-40) = 360 ячеек.
Для реализации этой задумки сначала был составлен алгоритм:
- Создать поле из ячеек
- Расположить мины на поле
- Расположить цифры на поле, показывающее количество мин вокруг
- Скрыть все поле другими цветами
- Создать события по нажатию на ячейку: если пустая, с цифрой, с миной
Также был составлен список проблем или недоработок, которые были найдены уже после написания кода:
- Установка флажка на ячейку, по нажатию правой кнопки отсутствует (некоторые люди ставят флажки на тех местах, где по их мнению может быть мина, но они не уверены на 100%).
- В оригинальной игре «Сапер» на Windows, при первом открытии ячейки нельзя попасть на мину: сначала игрок нажимает на ячейку, а затем генерируется поле, где нажатая ячейка НЕ является миной, а потом уже идет полноценная игра. На момент написания кода я этого не учел, поэтому с некоторой вероятностью первым кликом можно попасть на мину. Пока писал данный пункт — понял, как это реализовать, но решил, что переделывать уже не буду.
- Нет уведомлении об успешном «разминировании» всего поля. Добавлю это спустя некоторое время: в теории это не очень сложно, но, вероятно, это немного замедлит скорость выполнения всех макросов.
- Также в оригинальной игре есть возможность открыть все скрытые ячейки вокруг цифры, при условии, что все мины вокруг нее уже открыты — нужно было нажать на цифру левой кнопкой при зажатой правой. Функция довольно полезная, но на VBA реализовать ее мне не удалось.
Итак, «Сапер»:
Наш первый пункт — создать поле из ячеек. В Excel с этим не может возникнуть никаких проблем, ведь лист Excel по своей сути и есть набор ячеек. Единственное, что нам нужно сделать — определиться, какого размера будет наш «игровой квадрат», нарисовать границы и сделать ширину ячеек равной ее высоте. В конечном итоге, наше поле будет выглядеть так (поле 20х20):
Пункт второй: необходимо в нашей игре «Сапер» расположить на поле все мины. С этим пунктом также не должно возникнуть никаких проблем — все просто и элементарно. Нужно в случайных местах нашего поля расставить необходимое нам количество мин. Для этого пишем нужный нам код:
Sub mines() Dim mines_count As Integer mines_count = 0 Do While (mines_countВ данном макросе мы заполняем поле минами через цикл «While» (пока выполняется условие) — он работает до тех пор, пока на нашем поле не будет нужного нам количества мин (в данном примере — 40). Мы «рандомно» определяем строку, в которой будет находиться мина, затем так же «рандомно» определяем столбец для мины. Если в ячейке ничего нет — заполняем ее буквой «Б» (это означает, что там бомба, то есть мина) и увеличиваем счетчик мин на 1. Если в данном месте уже есть мина — ничего не делаем. И когда счетчик мин будет равен 40 — выполнение макроса завершится:
Пункт третий: необходимо в ячейках разместить цифры, которые будут показывать количество мин вокруг. Ниже представлен код для выполнения данного пункта:
Sub mines_count() For i = 2 To 21 'запускаем цикл по каждой ячейки нашего минного поля For j = 2 To 21 'столбцы и строки Count = 0 'счетчик мин равный нулю If (Sheets("Game").Cells(i, j) = "") Then 'проверяем ячейки вокруг искомой For x = i - 1 To i + 1 For y = j - 1 To j + 1 'если вокруг искомой ячейки есть мина - наращиваем счетчик If ((Sheets("Game").Cells(x, y) = "Б") And (x <> 0) And (y <> 0)) Then Count = Count + 1: Next y Next x If Count <> 0 Then Sheets("Game").Cells(i, j) = Count 'если счетчик не равен нулю - ставим цифру в ячейку End If Next j Next i End SubВ этом макросе мы проверяем каждую ячейку нашего минного поля, перебирая двумя циклами строки и столбцы. Мы считаем, сколько мин вокруг ячейки и вписываем в нее их количество. Если же мин вокруг ячейки нет — оставляем ее пустой.
Пункт четвертый: скрыть все мины и цифры. Это максимально просто: закрашиваем весь фон игрового диапазона новым цветом и точно таким же цветом окрашиваем шрифт мин и цифр, тем самым маскируя все поле:
Sub painting() Sheets("Game").Range(Cells(2, 2), Cells(21, 21)).Interior.Color = RGB(231, 230, 230) 'заливка фона Sheets("Game").Range(Cells(2, 2), Cells(21, 21)).Font.Color = RGB(231, 230, 230) 'изменение цвета шрифта End Sub
Все, поле игры «Сапер» на листе Excel готово. Теперь приступаем к самому главному!
Пункт пятый: события по нажатию на ячейку. Возможно, для кого-то данный пункт покажется сложным для восприятия, но я постараюсь рассказать все максимально просто.
Для его реализации составим подробный алгоритм:
- Отследить «выделение ячейки» и запустить определенный код в зависимости от ее содержимого
- Если ячейка равна «Б» — вывести сообщение о проигрыше и открыть все поле
- Если ячейка равна цифре — отобразить данную цифру, изменив ее цвет на черный
- Если ячейка пустая — запустить код по открытию прилежащей пустой области, ограниченной цифрами с количеством мин
- Если ячейка не относится к игровому полю — не делать ничего
Подробно разберем каждый из этих пунктов.
Сначала — отслеживание «выделения ячейки» . Отслеживать выделение ячейки в Excel можно с помощью стандартного события «Worksheet_SelectionChange«. Код для данного события вписывается в нужный нам лист и выполняется каждый раз когда мы будем выделять какую-нибудь ячейку на этом листе:
Private Sub Worksheet_SelectionChange(ByVal Target As Range) If Target.Cells.Count > 1 Then Exit Sub 'если выделено несколько ячеек - игнорируем 'если выделенная ячейка принадлежит игровому полю игры Сапер - запускаем обработку If Not Application.Intersect(Range(Cells(2, 2), Cells(21, 21)), Target) Is Nothing Then table_click (Target) 'запуск макроса обработки End If End SubВ данном коде есть две проверки.
- Первая — количество выделенных ячеек. По правилам игры, мы должны щелкнуть на одну ячейку, потом на другую, третью и так далее. Мы не можем нажать сразу на несколько. Поэтому и в Excel, чтобы «случайно» не нажать на несколько ячеек, т.е. в нашем случае, чтобы случайно их не выделить — добавляем ограничение: если количество выделенных ячеек больше 1 — ничего не выполняем, выходим из процедуры.
- Вторая — проверяем принадлежность выделенной ячейки нашему игровому полю. Если ячейка не имеет никакого отношения к нему — не делаем ничего, в противном случае запускаем обработку ячейки с помощью макроса table_click (target), где target — наша выделенная ячейка. Код макроса table_click:
Sub table_click(a) If (a = "Б") Then 'если ячейка содержит "Б" - игра проиграна Selection.Interior.Color = RGB(255, 255, 255) 'фон выделенной ячейки становится белым Selection.Font.Color = RGB(0, 0, 0) 'цвет шрифта бомбы - черным MsgBox "Подрыв! Игра окончена!", vbCritical + vbOKOnly, "Booooom!" 'выводим сообщение Boom Sheets("Game").Range(Cells(2, 2), Cells(21, 21)).Interior.Color = RGB(255, 255, 255) 'а затем все поле делаем белым Sheets("Game").Range(Cells(2, 2), Cells(21, 21)).Font.Color = RGB(0, 0, 0) 'и цвет шрифта на всем поле - черным (чтобы видеть всю картину) End If If (a >= 1) Then 'если выделенная ячейка содержит цифру Selection.Interior.Color = RGB(255, 255, 255) 'меняем цвет шрифт выделенной ячейки на черный Selection.Font.Color = RGB(0, 0, 0) 'а фон выделенной ячейки на белый End If If (a = "") Then prepare 'включение кода оптимизации free_space Selection.Row, Selection.Column 'запуск рекурсии начиная с выделенной ячейки (где Selection.Row - ее строка, Selection.Column - столбец) white 'раскрытие ячеек с цифрами вокруг открытой пустой области ended 'выключение кода оптимизации End If End Sub
Если содержимое ячейки равно «Б» — фон ячейки становится белым, шрифт черным. Выводим сообщение о проигрыше и фон всего поля окрашиваем белым (то есть все поле открывается).
Если содержимое равно 1 или больше — фон ячейки также становится белым, а шрифт черным, то есть мы просто «открываем» ячейку с миной.
Если содержимое ячейки равно «», то есть в ней нет ничего, запускаем целый набор макросов:
Включение кода оптимизации — данный код взят отсюда. При запуске деактивируются некоторые опции Excel, которые негативно влияют на быстродействие.
Public Sub Prepare() Application.ScreenUpdating = False 'отключение обновления экрана Application.Calculation = xlCalculationManual 'отключение автоматического пересчета формул Application.EnableEvents = False 'отключение обработки событий ActiveSheet.DisplayPageBreaks = False 'отключить отображение разрывов страниц Application.DisplayStatusBar = False 'отключить статусную строку в Excel Application.DisplayAlerts = False 'отключить отображение ошибок и предупреждений End Sub
Успешно используется мной во всех проектах и значительно сокращает время работы макросов при работе с большими объемами данных — рекомендую использовать данный код почаще, но старайтесь не забывать его «деактивировать».
Запуск рекурсии по раскрытию пустых ячеек вокруг. Рекурсия — это такое явление в программировании, когда функция/макрос/процедура запускает сама себя. И запускать себя она будет до тех пор, пока не встретится ограничивающее условие (в противном случае она может зациклиться и не закончиться никогда).
Смысл данной рекурсии заключается вот в чем: есть пустая ячейка на которую мы нажали. Запускается макрос, которые проверяет на «пустоты» соседние ячейки относительно выбранной. Макрос находит такую пустую ячейку, окрашивает ее в белый цвет и запускает сам себя же, только теперь уже относительно этой пустой ячейки. И выполняться он будет до тех пор, пока либо не упрется в границы игрового поля, либо если вокруг пустой ячейки не останется других пустых.
Sub free_space(n, m) On Error GoTo new_recur 'при ошибке запускается заново. Не знаю, как это работает, но ошибка при повторном запуске пропадает For i = n - 1 To n + 1 For j = m - 1 To m + 1 If (Cells(i, j) = "") And (Cells(i, j).Interior.Color = RGB(231, 230, 230)) Then 'если ячейка пустая и закрашенная (то есть принадлежит игровому полю) Cells(i, j).Interior.Color = RGB(255, 255, 255) 'обесцвечиваем ее free_space i, j 'и запускаем этот же макрос относительно нее End If Next j Next i GoTo continue new_recur: free_space n, m continue: End Sub
И если мы нажали на пустую ячейку — после выполнения данного макроса откроется вся пустая область вокруг нее.
Отображение цифр с количеством мин. Естественно, вокруг открытой области надо отобразить цифры, которые «формируют» эту область, чтобы можно было дальше играть. Для этого мы запускаем макрос white, суть которого проста: перебираем все ячейки нашего игрового поля и, если ячейка пустая и уже открытая — открываем ячейки вокруг нее, изменяя цвет фона на белый, а цвет шрифта в ячейке на черный.
Sub white() For i = 2 To 21 For j = 2 To 21 If (Cells(i, j).Interior.Color = RGB(255, 255, 255)) And (Cells(i, j).Value = "") Then For x = i - 1 To i + 1 For y = j - 1 To j + 1 Cells(x, y).Interior.Color = RGB(255, 255, 255) Cells(x, y).Font.Color = RGB(0, 0, 0) Next y Next x End If Next j Next i End Sub
Выключение кода оптимизации. Активируем опции Excel, которые мы выключили перед выполнением предыдущих макросов:
Public Sub ended() Application.ScreenUpdating = True Application.Calculation = xlCalculationAutomatic Application.EnableEvents = True 'ActiveSheet.DisplayPageBreaks = True 'Разрывы страниц для печати можно не возвращать - они тормозят работу в любом случае и не особо нужны Application.DisplayStatusBar = True Application.DisplayAlerts = True End Sub
В целом, работа по созданию игры «Сапер» завершена!
В моем примере также добавлена кнопка, которая запускает макрос full_game(), который полностью формирует поле для игры — ставит мины и цифры и закрашивает их шрифт и фон одинаковыми цветами. Также в ней запускается вышеупомянутый макрос оптимизации, для сокращения времени работы макросов.
Sub full_game() prepare 'включение кода оптимизации clearing_table 'очистка поля mines 'расстановка мин mines_count 'количество мин вокруг painting 'заливка ended 'отключение кода оптимизации End Sub
При добавлении каких-либо доработок типа «исключение взрыва при открытии первой ячейки» или «уведомление о победе» будет сообщено дополнительно.