• Что бы вступить в ряды "Принятый кодер" Вам нужно:
    Написать 10 полезных сообщений или тем и Получить 10 симпатий.
    Для того кто не хочет терять время,может пожертвовать средства для поддержки сервеса, и вступить в ряды VIP на месяц, дополнительная информация в лс.

  • Пользаватели которые будут спамить, уходят в бан без предупреждения. Спам сообщения определяется администрацией и модератором.

  • Гость, Что бы Вы хотели увидеть на нашем Форуме? Изложить свои идеи и пожелания по улучшению форума Вы можете поделиться с нами здесь. ----> Перейдите сюда
  • Все пользователи не прошедшие проверку электронной почты будут заблокированы. Все вопросы с разблокировкой обращайтесь по адресу электронной почте : info@guardianelinks.com . Не пришло сообщение о проверке или о сбросе также сообщите нам.

HarmonyOS NEXT Development Case: Minesweeper

Lomanu4 Оффлайн

Lomanu4

Команда форума
Администратор
Регистрация
1 Мар 2015
Сообщения
1,481
Баллы
155

Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.



The following code demonstrates how to implement a classic Minesweeper game using HarmonyOS NEXT's ArkUI framework. This implementation includes core game logic, state management, and interactive UI components.

Core Code Implementation


import { promptAction } from '@kit.ArkUI'; // Import dialog display utility

// Define Cell class
@ObservedV2 // Enable automatic state tracking
class Cell {
row: number; // Cell's row index
column: number; // Cell's column index
hasMine: boolean = false; // Mine presence flag
neighborMines: number = 0; // Number of adjacent mines
@Trace isFlag: boolean = false; // Flag marking state
@Trace value: string; // Display value (number or 'Mine')

constructor(row: number, column: number) {
this.row = row;
this.column = column;
this.value = ''; // Initialize empty value
}
}

// Main game component
@Entry // Entry component declaration
@Component // Component declaration
struct MineSweeper {
@State private gameBoard: Cell[][] = []; // Game board data
@State private mineCount: number = 10; // Total mines
@State private revealedCells: Set<string> = new Set(); // Revealed cells
@State private flaggedCells: Set<string> = new Set(); // Flagged cells
@State private cellSize: number = 60; // Cell dimension
@State private cellMargin: number = 2; // Cell spacing
private startTime: number = Date.now(); // Game start timestamp
@State private isGameOver: boolean = false; // Game over flag

// Lifecycle method
aboutToAppear(): void {
this.initializeGame();
}

private initializeGame() {
this.isGameOver = false;
this.startTime = Date.now();
this.revealedCells.clear();
this.flaggedCells.clear();
this.generateBoard();
}

private generateBoard() {
this.gameBoard = [];
for (let i = 0; i < 10; i++) {
this.gameBoard.push([]);
for (let j = 0; j < 10; j++) {
this.gameBoard.push(new Cell(i, j));
}
}
this.placeMines();
this.calculateNumbers();
}

private placeMines() {
let placed = 0;
while (placed < this.mineCount) {
let x = Math.floor(Math.random() * 10);
let y = Math.floor(Math.random() * 10);
if (!this.gameBoard[x][y].hasMine) {
this.gameBoard[x][y].hasMine = true;
placed++;
}
}
}

private calculateNumbers() {
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
if (!this.gameBoard[j].hasMine) {
this.gameBoard[j].neighborMines = this.countNeighborMines(i, j);
this.gameBoard[j].value = this.gameBoard[j].neighborMines.toString();
} else {
this.gameBoard[j].value = '雷'; // Chinese character for mine
}
}
}
}

private countNeighborMines(row: number, col: number): number {
let count = 0;
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue;
let newRow = row + dx, newCol = col + dy;
if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10 &&
this.gameBoard[newRow][newCol].hasMine) {
count++;
}
}
}
return count;
}

private revealCell(row: number, col: number) {
if (this.isGameOver || this.revealedCells.has(`${row},${col}`)) return;

const key = `${row},${col}`;
this.revealedCells.add(key);

if (this.gameBoard[row][col].hasMine) {
this.showGameOverDialog();
} else {
if (this.gameBoard[row][col].neighborMines === 0) {
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue;
let newRow = row + dx, newCol = col + dy;
if (newRow >= 0 && newRow < 10 && newCol >= 0 && newCol < 10) {
this.revealCell(newRow, newCol);
}
}
}
}
}
if (this.isVictory()) this.showVictoryDialog();
}

private showGameOverDialog() {
this.isGameOver = true;
promptAction.showDialog({
title: 'Game Over: You Lost!',
buttons: [{ text: 'Restart', color: '#ffa500' }]
}).then(() => this.initializeGame());
}

private showVictoryDialog() {
this.isGameOver = true;
promptAction.showDialog({
title: 'Congratulations! You Won!',
message: `Time: ${((Date.now() - this.startTime) / 1000).toFixed(3)}s`,
buttons: [{ text: 'Restart', color: '#ffa500' }]
}).then(() => this.initializeGame());
}

private isVictory(): boolean {
let revealedNonMineCount = 0;
for (let i = 0; i < this.gameBoard.length; i++) {
for (let j = 0; j < this.gameBoard.length; j++) {
if (this.revealedCells.has(`${i},${j}`)) {
revealedNonMineCount++;
}
}
}
return revealedNonMineCount === 90;
}

private isShowValue(cell: Cell): string {
if (this.isGameOver) {
return cell.value === '0' ? '' : cell.value;
} else {
return this.revealedCells.has(`${cell.row},${cell.column}`) ?
(cell.value === '0' ? '' : cell.value) : '';
}
}

build() {
Column({ space: 10 }) {
Button('Restart').onClick(() => this.initializeGame())
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.gameBoard, (row: Cell[], rowIndex: number) => {
ForEach(row, (cell: Cell, colIndex: number) => {
Stack() {
Text(this.isShowValue(cell))
.width(`${this.cellSize}lpx`)
.height(`${this.cellSize}lpx`)
.margin(`${this.cellMargin}lpx`)
.fontSize(`${this.cellSize / 2}lpx`)
.textAlign(TextAlign.Center)
.backgroundColor(this.revealedCells.has(`${rowIndex},${colIndex}`) ?
(this.isShowValue(cell) === '雷' ? Color.Red : Color.White) : Color.Gray)
.fontColor(!this.revealedCells.has(`${rowIndex},${colIndex}`) ||
this.isShowValue(cell) === '雷' ? Color.White : Color.Black)
.borderRadius(5)
.parallelGesture(GestureGroup(GestureMode.Exclusive,
TapGesture({ count: 1, fingers: 1 })
.onAction(() => this.revealCell(rowIndex, colIndex)),
LongPressGesture({ repeat: true })
.onAction(() => cell.isFlag = true)
));
Text(`${!this.revealedCells.has(`${rowIndex},${colIndex}`) ? '?' : ''}`)
.width(`${this.cellSize}lpx`)
.height(`${this.cellSize}lpx`)
.margin(`${this.cellMargin}lpx`)
.fontSize(`${this.cellSize / 2}lpx`)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.visibility(cell.isFlag && !this.isGameOver ?
Visibility.Visible : Visibility.None)
}
});
});
}
.width(`${(this.cellSize + this.cellMargin * 2) * 10}lpx`);
}
.backgroundColor(Color.Orange)
.width('100%')
.height('100%');
}
}
Key Technical Features


  1. State Management:
    • Uses @ObservedV2 decorator for automatic state tracking
    • Implements @Trace for flag state synchronization
    • Manages game state with @State properties

  2. Game Logic:
    • Recursive cell revealing algorithm
    • Mine placement randomization
    • Victory detection (90 safe cells revealed)

  3. UI Features:
    • Responsive grid layout with Flex container
    • Multi-gesture support (tap & long-press)
    • Dynamic cell styling based on game state
    • Animated flag markers (?)

  4. Performance:
    • Efficient cell rendering with ForEach
    • Optimized state updates using ArkUI's reactive system

This implementation demonstrates how to build complex game logic while leveraging HarmonyOS NEXT's declarative UI capabilities and reactive programming model. The code follows modern ArkUI development patterns and can be extended with additional features like difficulty levels or score tracking.


Пожалуйста Авторизируйтесь или Зарегистрируйтесь для просмотра скрытого текста.

 
Вверх Снизу