Auto-sync: 20260324_125426

This commit is contained in:
Paolo 2026-03-24 12:58:56 +01:00
parent 4b9d4930cd
commit 1395fc32f6
7 changed files with 11423 additions and 53 deletions

BIN
.DS_Store vendored

Binary file not shown.

View file

@ -1391,7 +1391,7 @@ PODS:
- gRPC-Core/Privacy (= 1.69.0) - gRPC-Core/Privacy (= 1.69.0)
- gRPC-Core/Interface (1.69.0) - gRPC-Core/Interface (1.69.0)
- gRPC-Core/Privacy (1.69.0) - gRPC-Core/Privacy (1.69.0)
- GTMSessionFetcher/Core (5.1.0) - GTMSessionFetcher/Core (5.2.0)
- leveldb-library (1.22.6) - leveldb-library (1.22.6)
- nanopb (3.30910.0): - nanopb (3.30910.0):
- nanopb/decode (= 3.30910.0) - nanopb/decode (= 3.30910.0)
@ -1501,7 +1501,7 @@ SPEC CHECKSUMS:
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1 GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
"gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8 "gRPC-C++": cc207623316fb041a7a3e774c252cf68a058b9e8
gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330 gRPC-Core: 860978b7db482de8b4f5e10677216309b5ff6330
GTMSessionFetcher: b8ab00db932816e14b0a0664a08cb73dda6d164b GTMSessionFetcher: 904bdd2a82c635bcd6f44edf94cc8775c5d1d6e6
leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19 leveldb-library: cc8b8f8e013647a295ad3f8cd2ddf49a6f19be19
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275 nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499 package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499

View file

@ -9,7 +9,7 @@ class _ClosureResult {
final bool closesSomething; final bool closesSomething;
final int netValue; final int netValue;
final bool causesSwap; final bool causesSwap;
final bool isIceTrap; // NUOVO: Identifica le mosse suicide sul ghiaccio final bool isIceTrap;
_ClosureResult(this.closesSomething, this.netValue, this.causesSwap, this.isIceTrap); _ClosureResult(this.closesSomething, this.netValue, this.causesSwap, this.isIceTrap);
} }
@ -30,24 +30,30 @@ class AIEngine {
int myScore = board.currentPlayer == Player.red ? board.scoreRed : board.scoreBlue; int myScore = board.currentPlayer == Player.red ? board.scoreRed : board.scoreBlue;
int oppScore = board.currentPlayer == Player.red ? board.scoreBlue : board.scoreRed; int oppScore = board.currentPlayer == Player.red ? board.scoreBlue : board.scoreRed;
// --- NUOVA LOGICA: GESTIONE INVERSIONE (TACTICAL FEEDING) ---
// Se c'è un numero dispari di caselle Scambio aperte, il gioco è "invertito".
// I punti accumulati andranno in regalo all'avversario!
int swapCount = board.boxes.where((b) => b.type == BoxType.swap && !b.isClosed()).length;
bool isInverted = swapCount % 2 != 0;
List<Line> goodClosingMoves = []; List<Line> goodClosingMoves = [];
List<Line> badClosingMoves = []; List<Line> badClosingMoves = [];
List<Line> iceTraps = []; // Le mosse da evitare assolutamente List<Line> iceTraps = [];
for (var line in availableLines) { for (var line in availableLines) {
var result = _checkClosure(board, line); var result = _checkClosure(board, line, isInverted);
if (result.isIceTrap) { if (result.isIceTrap) {
iceTraps.add(line); // Segna la linea come trappola e passa alla prossima iceTraps.add(line);
continue; continue;
} }
if (result.closesSomething) { if (result.closesSomething) {
if (result.causesSwap) { if (result.causesSwap) {
if (myScore < oppScore) { if (myScore < oppScore) {
goodClosingMoves.add(line); goodClosingMoves.add(line); // Se perdiamo, lo scambio è la mossa vincente!
} else { } else {
badClosingMoves.add(line); badClosingMoves.add(line); // Se vinciamo, NON tocchiamo lo scambio!
} }
} else { } else {
if (result.netValue >= 0) { if (result.netValue >= 0) {
@ -66,10 +72,10 @@ class AIEngine {
} }
} }
// --- REGOLA 2: Mosse Sicure (Ora include le esche del ghiaccio!) --- // --- REGOLA 2: Mosse Sicure ---
List<Line> safeMoves = []; List<Line> safeMoves = [];
for (var line in availableLines) { for (var line in availableLines) {
if (!badClosingMoves.contains(line) && !goodClosingMoves.contains(line) && !iceTraps.contains(line) && _isSafeMove(board, line, myScore, oppScore)) { if (!badClosingMoves.contains(line) && !goodClosingMoves.contains(line) && !iceTraps.contains(line) && _isSafeMove(board, line, myScore, oppScore, isInverted)) {
safeMoves.add(line); safeMoves.add(line);
} }
} }
@ -92,17 +98,16 @@ class AIEngine {
} }
} }
// Ultima spiaggia prima del disastro: qualsiasi cosa tranne bombe e trappole ghiacciate // Ultima spiaggia
List<Line> nonTerribleMoves = availableLines.where((l) => !badClosingMoves.contains(l) && !iceTraps.contains(l)).toList(); List<Line> nonTerribleMoves = availableLines.where((l) => !badClosingMoves.contains(l) && !iceTraps.contains(l)).toList();
if (nonTerribleMoves.isNotEmpty) { if (nonTerribleMoves.isNotEmpty) {
return nonTerribleMoves[random.nextInt(nonTerribleMoves.length)]; return nonTerribleMoves[random.nextInt(nonTerribleMoves.length)];
} }
// Se l'IA è messa all'angolo ed è costretta a suicidarsi... pesca a caso
return availableLines[random.nextInt(availableLines.length)]; return availableLines[random.nextInt(availableLines.length)];
} }
static _ClosureResult _checkClosure(GameBoard board, Line line) { static _ClosureResult _checkClosure(GameBoard board, Line line, bool isInverted) {
int netValue = 0; int netValue = 0;
bool closesSomething = false; bool closesSomething = false;
bool causesSwap = false; bool causesSwap = false;
@ -120,23 +125,27 @@ class AIEngine {
if (linesCount == 4) { if (linesCount == 4) {
if (box.type == BoxType.ice && !line.isIceCracked) { if (box.type == BoxType.ice && !line.isIceCracked) {
// L'IA capisce che questa mossa non chiuderà il box, ma le farà perdere il turno.
isIceTrap = true; isIceTrap = true;
} else { } else {
closesSomething = true; closesSomething = true;
if (box.hiddenJokerOwner == board.currentPlayer) { if (box.type == BoxType.swap) {
netValue += 2; causesSwap = true;
} else { } else {
if (box.type == BoxType.gold) netValue += 2; int boxValue = 0;
else if (box.type == BoxType.bomb) netValue -= 1; if (box.hiddenJokerOwner == board.currentPlayer) {
else if (box.type == BoxType.swap) netValue += 0; boxValue = 2;
else if (box.type == BoxType.ice) netValue += 0; // Rompere il ghiaccio vale 0 punti, ma fa rigiocare } else {
else if (box.type == BoxType.multiplier) netValue += 1; // Leggero boost per dare priorità al x2 if (box.type == BoxType.gold) boxValue = 2;
else netValue += 1; else if (box.type == BoxType.bomb) boxValue = -1;
} else if (box.type == BoxType.ice) boxValue = 0;
else if (box.type == BoxType.multiplier) boxValue = 1;
else boxValue = 1;
}
if (box.type == BoxType.swap) causesSwap = true; // LA MAGIA: Se il gioco è invertito, fare punti positivi viene calcolato come MALUS per l'IA!
netValue += isInverted ? -boxValue : boxValue;
}
} }
} }
} }
@ -144,7 +153,7 @@ class AIEngine {
return _ClosureResult(closesSomething, netValue, causesSwap, isIceTrap); return _ClosureResult(closesSomething, netValue, causesSwap, isIceTrap);
} }
static bool _isSafeMove(GameBoard board, Line line, int myScore, int oppScore) { static bool _isSafeMove(GameBoard board, Line line, int myScore, int oppScore, bool isInverted) {
for (var box in board.boxes) { for (var box in board.boxes) {
if (box.type == BoxType.invisible) continue; if (box.type == BoxType.invisible) continue;
@ -156,37 +165,35 @@ class AIEngine {
if (box.right.owner != Player.none) currentLinesCount++; if (box.right.owner != Player.none) currentLinesCount++;
if (currentLinesCount == 2) { if (currentLinesCount == 2) {
// L'IA valuta cosa succede se lascia questa casella con 3 linee all'avversario
int valueForOpponent = 0; int valueForOpponent = 0;
if (box.type == BoxType.ice) { if (box.type == BoxType.ice) {
// Il ghiaccio è la trappola perfetta. Lasciarlo con 3 linee spingerà l'avversario a incrinarlo e a perdere il turno.
// L'IA valuta questa mossa come SICURISSIMA!
valueForOpponent = -5; valueForOpponent = -5;
} else if (box.type == BoxType.swap) {
if (myScore < oppScore) {
continue; // Sicuro lasciarlo: se lo prende perde i punti.
} else {
return false; // Pericoloso: se lo prende ci ruba il vantaggio!
}
} else if (box.hiddenJokerOwner == board.currentPlayer) { } else if (box.hiddenJokerOwner == board.currentPlayer) {
valueForOpponent = -1; valueForOpponent = -1;
} else { } else {
if (box.type == BoxType.gold) valueForOpponent = 2; if (box.type == BoxType.gold) valueForOpponent = 2;
else if (box.type == BoxType.bomb) valueForOpponent = -1; else if (box.type == BoxType.bomb) valueForOpponent = -1;
else if (box.type == BoxType.swap) valueForOpponent = 0;
else if (box.type == BoxType.multiplier) valueForOpponent = 1; else if (box.type == BoxType.multiplier) valueForOpponent = 1;
else valueForOpponent = 1; else valueForOpponent = 1;
} }
// Se per l'avversario è una trappola (bomba o ghiaccio), lascia pure la mossa libera // LA MAGIA 2: Se il tabellone è invertito, regalare un punto all'avversario è un'ottima esca!
if (isInverted && box.type != BoxType.swap && box.type != BoxType.ice) {
valueForOpponent = -valueForOpponent;
}
if (valueForOpponent < 0) { if (valueForOpponent < 0) {
continue; continue; // Mossa considerata sicura (trappola perfetta)
} }
if (box.type == BoxType.swap) { return false;
if (myScore < oppScore) {
continue;
} else {
return false;
}
}
return false; // La mossa regalerebbe punti, quindi NON è sicura
} }
} }
} }

View file

@ -5,7 +5,7 @@
import 'dart:math'; import 'dart:math';
enum Player { red, blue, none } enum Player { red, blue, none }
enum BoxType { normal, gold, bomb, invisible, swap, ice, multiplier } // Aggiunti ice e multiplier enum BoxType { normal, gold, bomb, invisible, swap, ice, multiplier }
enum ArenaShape { classic, cross, donut, hourglass, chaos } enum ArenaShape { classic, cross, donut, hourglass, chaos }
class Dot { class Dot {
@ -24,7 +24,7 @@ class Line {
final Dot p2; final Dot p2;
Player owner = Player.none; Player owner = Player.none;
bool isPlayable = false; bool isPlayable = false;
bool isIceCracked = false; // NUOVO: Stato per il blocco di ghiaccio bool isIceCracked = false;
Line(this.p1, this.p2); Line(this.p1, this.p2);
@ -55,7 +55,7 @@ class Box {
} }
if (type == BoxType.gold) return 2; if (type == BoxType.gold) return 2;
if (type == BoxType.bomb) return -1; if (type == BoxType.bomb) return -1;
if (type == BoxType.swap || type == BoxType.ice || type == BoxType.multiplier) return 0; // Il moltiplicatore e il ghiaccio non danno punti base if (type == BoxType.swap || type == BoxType.ice || type == BoxType.multiplier) return 0;
return 1; return 1;
} }
} }
@ -80,7 +80,6 @@ class GameBoard {
Line? lastMove; Line? lastMove;
// Variabili per il Moltiplicatore
bool redHasMultiplier = false; bool redHasMultiplier = false;
bool blueHasMultiplier = false; bool blueHasMultiplier = false;
@ -158,13 +157,23 @@ class GameBoard {
if (chance < 0.08) box.type = BoxType.gold; if (chance < 0.08) box.type = BoxType.gold;
else if (chance > 0.92) box.type = BoxType.bomb; else if (chance > 0.92) box.type = BoxType.bomb;
else if (level >= 5 && chance > 0.88 && chance <= 0.92) box.type = BoxType.swap; else if (level >= 5 && chance > 0.88 && chance <= 0.92) box.type = BoxType.swap;
else if (level >= 10 && chance > 0.83 && chance <= 0.88) box.type = BoxType.ice; // Nuova Scatola Ghiaccio else if (level >= 10 && chance > 0.83 && chance <= 0.88) box.type = BoxType.ice;
else if (level >= 15 && chance > 0.78 && chance <= 0.83) box.type = BoxType.multiplier; // Nuova Scatola x2 else if (level >= 15 && chance > 0.78 && chance <= 0.83) box.type = BoxType.multiplier;
} }
boxes.add(box); boxes.add(box);
} }
} }
// =========================================================
// NUOVO BLOCCO: ELIMINAZIONE SCAMBI PARI
// =========================================================
int swapCount = boxes.where((b) => b.type == BoxType.swap).length;
if (swapCount > 0 && swapCount % 2 == 0) {
Box lastSwap = boxes.lastWhere((b) => b.type == BoxType.swap);
lastSwap.type = BoxType.normal;
}
// =========================================================
for (var box in boxes) { for (var box in boxes) {
Dot tl = _getOrAddDot(box.x, box.y); Dot tl = _getOrAddDot(box.x, box.y);
Dot tr = _getOrAddDot(box.x + 1, box.y); Dot tr = _getOrAddDot(box.x + 1, box.y);
@ -220,14 +229,13 @@ class GameBoard {
} }
if (closesIce && !actualLine.isIceCracked) { if (closesIce && !actualLine.isIceCracked) {
actualLine.isIceCracked = true; // Si incrina ma non si chiude! actualLine.isIceCracked = true;
lastMove = actualLine; lastMove = actualLine;
if (forcedPlayer == null) currentPlayer = (currentPlayer == Player.red) ? Player.blue : Player.red; if (forcedPlayer == null) currentPlayer = (currentPlayer == Player.red) ? Player.blue : Player.red;
else currentPlayer = (forcedPlayer == Player.red) ? Player.blue : Player.red; else currentPlayer = (forcedPlayer == Player.red) ? Player.blue : Player.red;
return true; // Mossa valida, ma turno finito. return true;
} }
// Mossa normale o secondo colpo al ghiaccio
actualLine.isIceCracked = false; actualLine.isIceCracked = false;
actualLine.owner = playerMakingMove; actualLine.owner = playerMakingMove;
lastMove = actualLine; lastMove = actualLine;
@ -249,13 +257,12 @@ class GameBoard {
if (playerMakingMove == Player.red) redHasMultiplier = true; if (playerMakingMove == Player.red) redHasMultiplier = true;
else blueHasMultiplier = true; else blueHasMultiplier = true;
} else if (points != 0) { } else if (points != 0) {
// Se la scatola chiusa punti e il giocatore ha un x2 attivo...
if (playerMakingMove == Player.red && redHasMultiplier) { if (playerMakingMove == Player.red && redHasMultiplier) {
points *= 2; points *= 2;
redHasMultiplier = false; // Si consuma redHasMultiplier = false;
} else if (playerMakingMove == Player.blue && blueHasMultiplier) { } else if (playerMakingMove == Player.blue && blueHasMultiplier) {
points *= 2; points *= 2;
blueHasMultiplier = false; // Si consuma blueHasMultiplier = false;
} }
} }

View file

@ -1,7 +1,7 @@
name: tetraq name: tetraq
description: A new Flutter project. description: A new Flutter project.
publish_to: 'none' publish_to: 'none'
version: 1.1.7+1 version: 1.1.8+2
environment: environment:
sdk: ^3.10.7 sdk: ^3.10.7

File diff suppressed because it is too large Load diff

54
report/codice_recap.txt Normal file
View file

@ -0,0 +1,54 @@
PROJECT_NAME=$(basename "$PWD")
TIMESTAMP=$(date +"%d-%m-%y_%H.%M")
OUTPUT_FILE="./report/${PROJECT_NAME}_${TIMESTAMP}.txt"
mkdir -p ./report && {
echo "=== FLUTTER PROJECT BACKUP ==="
echo ""
echo "=== PROJECT STRUCTURE (LIB, ASSETS & PUBLIC) ==="
find lib assets public -type f 2>/dev/null | sort
echo ""
echo "=== pubspec.yaml ==="
cat pubspec.yaml 2>/dev/null
echo ""
echo "=== MAC OS CONFIG ==="
echo "--- Info.plist ---"
cat macos/Runner/Info.plist 2>/dev/null
echo "--- Entitlements ---"
cat macos/Runner/*.entitlements 2>/dev/null
echo "--- Podfile ---"
cat macos/Podfile 2>/dev/null
echo ""
echo "=== IOS CONFIG ==="
echo "--- Info.plist ---"
cat ios/Runner/Info.plist 2>/dev/null
echo "--- Podfile ---"
cat ios/Podfile 2>/dev/null
echo ""
echo "=== ANDROID CONFIG ==="
echo "--- AndroidManifest.xml ---"
cat android/app/src/main/AndroidManifest.xml 2>/dev/null
echo "--- build.gradle / build.gradle.kts ---"
cat android/app/build.gradle 2>/dev/null
cat android/app/build.gradle.kts 2>/dev/null
echo ""
echo "=== WEB / FIREBASE (public/) ==="
find public -type f \( -name "*.html" -o -name "*.js" -o -name "*.css" -o -name "*.json" \) 2>/dev/null | sort | while read -r file; do
echo ""
echo "// ==========================================================================="
echo "// FILE: $file"
echo "// ==========================================================================="
echo ""
cat "$file"
done
echo ""
echo "=== SOURCE CODE (lib/) ==="
find lib -type f -name "*.dart" 2>/dev/null | sort | while read -r file; do
echo ""
echo "// ==========================================================================="
echo "// FILE: $file"
echo "// ==========================================================================="
echo ""
cat "$file"
done
} > "$OUTPUT_FILE" && echo "Backup completato! Artefatto salvato in: $OUTPUT_FILE"