// =========================================================================== // FILE: lib/models/game_board.dart // =========================================================================== import 'dart:math'; enum Player { red, blue, none } enum BoxType { normal, gold, bomb, invisible, swap } // --- AGGIUNTO 'chaos' --- enum ArenaShape { classic, cross, donut, hourglass, chaos } class Dot { final int x; final int y; Dot(this.x, this.y); @override bool operator ==(Object other) => identical(this, other) || other is Dot && runtimeType == other.runtimeType && x == other.x && y == other.y; @override int get hashCode => x.hashCode ^ y.hashCode; } class Line { final Dot p1; final Dot p2; Player owner = Player.none; bool isPlayable = false; Line(this.p1, this.p2); bool connects(Dot a, Dot b) { return (p1 == a && p2 == b) || (p1 == b && p2 == a); } } class Box { final int x; final int y; Player owner = Player.none; late Line top, bottom, left, right; BoxType type = BoxType.normal; Box(this.x, this.y); bool isClosed() { if (type == BoxType.invisible) return false; return top.owner != Player.none && bottom.owner != Player.none && left.owner != Player.none && right.owner != Player.none; } int get value { if (type == BoxType.gold) return 2; if (type == BoxType.bomb) return -1; if (type == BoxType.swap) return 0; return 1; } } class GameBoard { final int radius; final int level; final int? seed; final ArenaShape shape; List dots = []; List lines = []; List boxes = []; Player currentPlayer = Player.red; int scoreRed = 0; int scoreBlue = 0; bool isGameOver = false; Line? lastMove; GameBoard({required this.radius, this.level = 1, this.seed, this.shape = ArenaShape.classic}) { _generateBoard(); } void _generateBoard() { int size = radius * 2 + 1; final random = seed != null ? Random(seed) : Random(); // Se è Caos, decidiamo quale algoritmo usare in base al seed int chaosAlgorithm = random.nextInt(5); dots.clear(); lines.clear(); boxes.clear(); lastMove = null; for (int y = 0; y < size; y++) { for (int x = 0; x < size; x++) { var box = Box(x, y); int dx = (x - radius).abs(); int dy = (y - radius).abs(); bool isVisible = (dx + dy) <= radius; if (isVisible) { switch (shape) { case ArenaShape.classic: break; case ArenaShape.cross: int spessoreBraccio = radius > 3 ? 1 : 0; if (dx > spessoreBraccio && dy > spessoreBraccio) isVisible = false; break; case ArenaShape.donut: int dimensioneBuco = radius > 3 ? 2 : 1; if ((dx + dy) <= dimensioneBuco) isVisible = false; break; case ArenaShape.hourglass: if (dx > dy) isVisible = false; if (x == radius && y == radius) isVisible = true; break; case ArenaShape.chaos: // --- GENERATORE PROCEDURALE (IL CAOS) --- // Essendo basato su dx e dy, genererà sempre forme simmetriche a 4 vie! if (chaosAlgorithm == 0) { // Modello "Rete Frattale": Rimuove blocchi basati su operatori bitwise if ((dx & dy) != 0) isVisible = false; } else if (chaosAlgorithm == 1) { // Modello "Quattro Pilastri": Svuota lunghe linee ma salva i centri if (dx == 1 || dy == 1) { if ((dx + dy) > 2 && (dx + dy) < radius) isVisible = false; } } else if (chaosAlgorithm == 2) { // Modello "X-Treme": Taglia le diagonali perfette if (dx == dy && dx > 0 && dx < radius) isVisible = false; } else if (chaosAlgorithm == 3) { // Modello "Scacchiera Nucleare": Alternanza precisa if (dx % 2 == 1 && dy % 2 == 1) isVisible = false; } else if (chaosAlgorithm == 4) { // Modello "Anelli Frammentati": Buca gli anelli pari if ((dx + dy) % 2 == 0 && (dx + dy) > 0) { if (dx != 0 && dy != 0) isVisible = false; } } // Assicuriamoci che il punto centrale esista quasi sempre per connettere la mappa if (dx == 0 && dy == 0) isVisible = true; break; } } if (!isVisible) { box.type = BoxType.invisible; } else if (level > 1) { double chance = random.nextDouble(); if (chance < 0.10) { box.type = BoxType.gold; } else if (chance > 0.90) { box.type = BoxType.bomb; } else if (level >= 5 && chance > 0.85 && chance <= 0.90) { box.type = BoxType.swap; } } boxes.add(box); } } // Costruzione Linee (Identico a prima) for (var box in boxes) { Dot tl = _getOrAddDot(box.x, box.y); Dot tr = _getOrAddDot(box.x + 1, box.y); Dot bl = _getOrAddDot(box.x, box.y + 1); Dot br = _getOrAddDot(box.x + 1, box.y + 1); box.top = _getOrAddLine(tl, tr); box.bottom = _getOrAddLine(bl, br); box.left = _getOrAddLine(tl, bl); box.right = _getOrAddLine(tr, br); if (box.type != BoxType.invisible) { box.top.isPlayable = true; box.bottom.isPlayable = true; box.left.isPlayable = true; box.right.isPlayable = true; } } } Dot _getOrAddDot(int x, int y) { for (var dot in dots) { if (dot.x == x && dot.y == y) return dot; } var newDot = Dot(x, y); dots.add(newDot); return newDot; } Line _getOrAddLine(Dot a, Dot b) { for (var line in lines) { if (line.connects(a, b)) return line; } var newLine = Line(a, b); lines.add(newLine); return newLine; } bool playMove(Line lineToPlay, {Player? forcedPlayer}) { if (isGameOver) return false; Player playerMakingMove = forcedPlayer ?? currentPlayer; Line? actualLine; for (var l in lines) { if (l.connects(lineToPlay.p1, lineToPlay.p2)) { actualLine = l; break; } } if (actualLine == null || actualLine.owner != Player.none || !actualLine.isPlayable) return false; actualLine.owner = playerMakingMove; lastMove = actualLine; bool boxedClosed = false; bool triggeredSwap = false; for (var box in boxes) { if (box.owner == Player.none && box.isClosed()) { box.owner = playerMakingMove; boxedClosed = true; if (playerMakingMove == Player.red) { scoreRed += box.value; } else { scoreBlue += box.value; } if (box.type == BoxType.swap) { triggeredSwap = true; } } } if (triggeredSwap) { int temp = scoreRed; scoreRed = scoreBlue; scoreBlue = temp; } if (lines.where((l) => l.isPlayable).every((l) => l.owner != Player.none)) { isGameOver = true; } if (forcedPlayer == null) { if (!boxedClosed && !isGameOver) { currentPlayer = (currentPlayer == Player.red) ? Player.blue : Player.red; } } else { if (!boxedClosed && !isGameOver) { currentPlayer = (forcedPlayer == Player.red) ? Player.blue : Player.red; } else { currentPlayer = forcedPlayer; } } return true; } }