HarmonyOS NEXT Development Case: Encircle the Neuro Cat
This article demonstrates how to implement a "Neuro Cat" game using HarmonyOS NEXT and ArkUI. The goal is to trap the cat within a hexagonal grid by placing walls strategically. Below is the code implementation with detailed explanations and translated annotations. Core Code Implementation 1. Import Dependencies import { promptAction } from '@kit.ArkUI'; // Import prompt dialog component 2. Define the Cell Class @ObservedV2 // Observer decorator to monitor state changes class Cell { @Trace value: number = 0; // Cell value: 0 (empty), 1 (wall) x: number = 0; // X-coordinate of the cell y: number = 0; // Y-coordinate of the cell leftNeighborIndex: number | undefined = undefined; // Left neighbor index rightNeighborIndex: number | undefined = undefined; // Right neighbor index leftTopNeighborIndex: number | undefined = undefined; // Left-top neighbor index rightTopNeighborIndex: number | undefined = undefined; // Right-top neighbor index leftBottomNeighborIndex: number | undefined = undefined; // Left-bottom neighbor index rightBottomNeighborIndex: number | undefined = undefined; // Right-bottom neighbor index constructor(value: number, x: number, y: number) { this.value = value; // Initialize cell value this.x = x; // Initialize X-coordinate this.y = y; // Initialize Y-coordinate } } 3. Main Component Implementation @Entry // Entry decorator for the root component @Component // Component decorator struct Index { @State gridCells: Cell[] = []; // Array storing all cells cellWidth: number = 70; // Cell width borderPieceWidth: number = -10; // Border piece width pieceSize: number = 65; // Piece size @State catPositionIndex: number = 40; // Cat's current position index // Initialize the game board aboutToAppear(): void { this.initializeBoard(); this.startGame(); } // Find movable neighbors for a cell findNeighbors(cell: Cell): Cell[] { let neighbors: Cell[] = []; if (cell.leftNeighborIndex !== undefined && this.gridCells[cell.leftNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.leftNeighborIndex]); // Left neighbor } if (cell.rightNeighborIndex !== undefined && this.gridCells[cell.rightNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.rightNeighborIndex]); // Right neighbor } if (cell.leftTopNeighborIndex !== undefined && this.gridCells[cell.leftTopNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.leftTopNeighborIndex]); // Left-top neighbor } if (cell.rightTopNeighborIndex !== undefined && this.gridCells[cell.rightTopNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.rightTopNeighborIndex]); // Right-top neighbor } if (cell.leftBottomNeighborIndex !== undefined && this.gridCells[cell.leftBottomNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.leftBottomNeighborIndex]); // Left-bottom neighbor } if (cell.rightBottomNeighborIndex !== undefined && this.gridCells[cell.rightBottomNeighborIndex].value === 0) { neighbors.push(this.gridCells[cell.rightBottomNeighborIndex]); // Right-bottom neighbor } return neighbors; } // Initialize the 9x9 hexagonal grid initializeBoard() { this.gridCells = []; for (let rowIndex = 0; rowIndex this.startGame()); } else { const nextMove = this.selectNextMove(emptyNeighbors); this.catPositionIndex = nextMove.x * 9 + nextMove.y; if (nextMove.x === 0 || nextMove.x === 8 || nextMove.y === 0 || nextMove.y === 8) { promptAction.showDialog({ title: "'You Lose!'," buttons: [{ text: 'Restart', color: '#ffa500' }] }).then(() => this.startGame()); } } } // Select the next move using a heuristic approach selectNextMove(emptyNeighbors: Cell[]): Cell { let closestToEdge: Cell | null = null; let minDistanceToEdge = Number.MAX_VALUE; for (const neighbor of emptyNeighbors) { const distance = Math.min(neighbor.x, 8 - neighbor.x, neighbor.y, 8 - neighbor.y); if (distance { Stack() { Text().width(`${this.pieceSize}lpx`).height(`${this.pieceSize}lpx`) .backgroundColor(item.value === 0 ? "#b4b4b4" : "#ff8d5a") .borderRadius('50%') } .margin({ left: `${(index + 9) % 18 === 0 ? this.cellWidth / 2 : 0}lpx` }) }) } // Cat and interactive elements Flex({ wrap: FlexWrap.Wrap }) { ForEach(this.gridCells, (item: Cell, index: number) => { Stack() { Text('Cat') .width(`${this.pieceSize}lpx`) .fontColor(Color.White) .visibility(this.catPositionIndex === index ? Visibility.Visible : Visibility.None) } .onClick(() => { if (item.value === 0 && index !== this.catPositionIndex)

This article demonstrates how to implement a "Neuro Cat" game using HarmonyOS NEXT and ArkUI. The goal is to trap the cat within a hexagonal grid by placing walls strategically. Below is the code implementation with detailed explanations and translated annotations.
Core Code Implementation
1. Import Dependencies
import { promptAction } from '@kit.ArkUI'; // Import prompt dialog component
2. Define the Cell Class
@ObservedV2 // Observer decorator to monitor state changes
class Cell {
@Trace value: number = 0; // Cell value: 0 (empty), 1 (wall)
x: number = 0; // X-coordinate of the cell
y: number = 0; // Y-coordinate of the cell
leftNeighborIndex: number | undefined = undefined; // Left neighbor index
rightNeighborIndex: number | undefined = undefined; // Right neighbor index
leftTopNeighborIndex: number | undefined = undefined; // Left-top neighbor index
rightTopNeighborIndex: number | undefined = undefined; // Right-top neighbor index
leftBottomNeighborIndex: number | undefined = undefined; // Left-bottom neighbor index
rightBottomNeighborIndex: number | undefined = undefined; // Right-bottom neighbor index
constructor(value: number, x: number, y: number) {
this.value = value; // Initialize cell value
this.x = x; // Initialize X-coordinate
this.y = y; // Initialize Y-coordinate
}
}
3. Main Component Implementation
@Entry // Entry decorator for the root component
@Component // Component decorator
struct Index {
@State gridCells: Cell[] = []; // Array storing all cells
cellWidth: number = 70; // Cell width
borderPieceWidth: number = -10; // Border piece width
pieceSize: number = 65; // Piece size
@State catPositionIndex: number = 40; // Cat's current position index
// Initialize the game board
aboutToAppear(): void {
this.initializeBoard();
this.startGame();
}
// Find movable neighbors for a cell
findNeighbors(cell: Cell): Cell[] {
let neighbors: Cell[] = [];
if (cell.leftNeighborIndex !== undefined && this.gridCells[cell.leftNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.leftNeighborIndex]); // Left neighbor
}
if (cell.rightNeighborIndex !== undefined && this.gridCells[cell.rightNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.rightNeighborIndex]); // Right neighbor
}
if (cell.leftTopNeighborIndex !== undefined && this.gridCells[cell.leftTopNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.leftTopNeighborIndex]); // Left-top neighbor
}
if (cell.rightTopNeighborIndex !== undefined && this.gridCells[cell.rightTopNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.rightTopNeighborIndex]); // Right-top neighbor
}
if (cell.leftBottomNeighborIndex !== undefined && this.gridCells[cell.leftBottomNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.leftBottomNeighborIndex]); // Left-bottom neighbor
}
if (cell.rightBottomNeighborIndex !== undefined && this.gridCells[cell.rightBottomNeighborIndex].value === 0) {
neighbors.push(this.gridCells[cell.rightBottomNeighborIndex]); // Right-bottom neighbor
}
return neighbors;
}
// Initialize the 9x9 hexagonal grid
initializeBoard() {
this.gridCells = [];
for (let rowIndex = 0; rowIndex < 9; rowIndex++) {
for (let columnIndex = 0; columnIndex < 9; columnIndex++) {
this.gridCells.push(new Cell(0, rowIndex, columnIndex));
}
}
// Configure neighbor indices based on hexagonal grid logic
for (let rowIndex = 0; rowIndex < 9; rowIndex++) {
for (let columnIndex = 0; columnIndex < 9; columnIndex++) {
const cellIndex = rowIndex * 9 + columnIndex;
const cell = this.gridCells[cellIndex];
// Logic for setting neighbor indices...
// (Omitted for brevity; see original code for details)
}
}
}
// Start or reset the game
startGame() {
let availableIndices: number[] = [];
for (let i = 0; i < 81; i++) {
this.gridCells[i].value = 0;
if (i === 39 || i === 40 || i === 41) continue; // Exclude center cells
availableIndices.push(i);
}
// Generate random walls
for (let i = 0; i < 8; i++) {
const randomIndex = Math.floor(Math.random() * availableIndices.length);
this.gridCells[availableIndices[randomIndex]].value = 1;
availableIndices.splice(randomIndex, 1);
}
this.catPositionIndex = 40; // Reset cat position
}
// Move the cat based on AI logic
moveCat(): void {
const neighbors = this.findNeighbors(this.gridCells[this.catPositionIndex]);
const emptyNeighbors = neighbors.filter(neighbor => neighbor.value === 0);
if (emptyNeighbors.length === 0) {
promptAction.showDialog({
title: "'You Win!',"
buttons: [{ text: 'Restart', color: '#ffa500' }]
}).then(() => this.startGame());
} else {
const nextMove = this.selectNextMove(emptyNeighbors);
this.catPositionIndex = nextMove.x * 9 + nextMove.y;
if (nextMove.x === 0 || nextMove.x === 8 || nextMove.y === 0 || nextMove.y === 8) {
promptAction.showDialog({
title: "'You Lose!',"
buttons: [{ text: 'Restart', color: '#ffa500' }]
}).then(() => this.startGame());
}
}
}
// Select the next move using a heuristic approach
selectNextMove(emptyNeighbors: Cell[]): Cell {
let closestToEdge: Cell | null = null;
let minDistanceToEdge = Number.MAX_VALUE;
for (const neighbor of emptyNeighbors) {
const distance = Math.min(neighbor.x, 8 - neighbor.x, neighbor.y, 8 - neighbor.y);
if (distance < minDistanceToEdge) {
minDistanceToEdge = distance;
closestToEdge = neighbor;
}
}
return closestToEdge || emptyNeighbors[0];
}
// Build UI components
build() {
Column({ space: 20 }) {
Stack() {
// Background grid
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.gridCells, (item: Cell, index: number) => {
Stack() {
Text().width(`${this.pieceSize}lpx`).height(`${this.pieceSize}lpx`)
.backgroundColor(item.value === 0 ? "#b4b4b4" : "#ff8d5a")
.borderRadius('50%')
}
.margin({ left: `${(index + 9) % 18 === 0 ? this.cellWidth / 2 : 0}lpx` })
})
}
// Cat and interactive elements
Flex({ wrap: FlexWrap.Wrap }) {
ForEach(this.gridCells, (item: Cell, index: number) => {
Stack() {
Text('Cat')
.width(`${this.pieceSize}lpx`)
.fontColor(Color.White)
.visibility(this.catPositionIndex === index ? Visibility.Visible : Visibility.None)
}
.onClick(() => {
if (item.value === 0 && index !== this.catPositionIndex) {
item.value = 1; // Place a wall
this.moveCat(); // Trigger cat movement
}
})
})
}
}
// Restart button
Button('Restart').onClick(() => this.startGame())
}
.backgroundColor("#666666")
}
}
Key Features
- Hexagonal Grid Logic: The grid uses a 9x9 hexagonal layout, with neighbor indices calculated based on row parity.
- Cat Movement AI: The cat moves toward the edge using a Manhattan distance heuristic to escape.
-
State Management: Leverages
@ObservedV2
and@State
decorators for reactive UI updates. - User Interaction: Players click empty cells to place walls, triggering the cat's movement.
This implementation showcases HarmonyOS NEXT's declarative UI capabilities and reactive programming model. Developers can extend this logic to create more complex games or interactive applications.