import 'package:flutter_custom_toolbox/flutter_toolbox.dart'; import 'package:snake/models/activity/board.dart'; import 'package:snake/models/activity/cell.dart'; import 'package:snake/models/activity/cell_location.dart'; import 'package:snake/models/activity/snake.dart'; import 'package:snake/models/settings/settings_activity.dart'; import 'package:snake/models/settings/settings_global.dart'; class Activity { Activity({ // Settings required this.activitySettings, required this.globalSettings, // State this.isRunning = false, this.isStarted = false, this.isFinished = false, this.animationInProgress = false, // Base data required this.snake, required this.board, required this.boardSize, // Game data this.score = 0, this.gameWon = false, }); // Settings final ActivitySettings activitySettings; final GlobalSettings globalSettings; // State bool isRunning; bool isStarted; bool isFinished; bool animationInProgress; // Base data Snake snake; Board board; final int boardSize; // Game data int score; bool gameWon; factory Activity.createEmpty() { final ActivitySettings defaultActivitySettings = ActivitySettings.createDefault(); final GlobalSettings defaultGlobalSettings = GlobalSettings.createDefault(); return Activity( // Settings activitySettings: defaultActivitySettings, globalSettings: defaultGlobalSettings, // Base data snake: Snake.create(defaultActivitySettings), board: Board.createEmpty(defaultActivitySettings), boardSize: defaultActivitySettings.boardSize, ); } factory Activity.createNew({ ActivitySettings? activitySettings, GlobalSettings? globalSettings, }) { final ActivitySettings newActivitySettings = activitySettings ?? ActivitySettings.createDefault(); final GlobalSettings newGlobalSettings = globalSettings ?? GlobalSettings.createDefault(); Activity newActivity = Activity( // Settings activitySettings: newActivitySettings, globalSettings: newGlobalSettings, // State isRunning: true, // Base data snake: Snake.create(newActivitySettings), board: Board.createEmpty(newActivitySettings), boardSize: newActivitySettings.boardSize, // Game data score: 0, ); newActivity.computeBoard(); return newActivity; } bool get canBeResumed => isStarted && !isFinished; void computeBoard() { Board newBoard = Board.createEmpty(activitySettings); // Add snake for (int i = 0; i < snake.cells.length - 1; i++) { newBoard.setCellValue(snake.cells[i], CellValue.body); } newBoard.setCellValue(snake.cells[snake.cells.length - 1], CellValue.head); board = newBoard; } void turnLeft() { switch (snake.direction) { case SnakeDirection.left: snake.direction = SnakeDirection.bottom; case SnakeDirection.right: snake.direction = SnakeDirection.top; case SnakeDirection.top: snake.direction = SnakeDirection.left; case SnakeDirection.bottom: snake.direction = SnakeDirection.right; } } void turnRight() { switch (snake.direction) { case SnakeDirection.left: snake.direction = SnakeDirection.top; case SnakeDirection.right: snake.direction = SnakeDirection.bottom; case SnakeDirection.top: snake.direction = SnakeDirection.right; case SnakeDirection.bottom: snake.direction = SnakeDirection.left; } } CellLocation nextHeadLocation() { CellLocation head = CellLocation(col: snake.head.col, row: snake.head.row); switch (snake.direction) { case SnakeDirection.left: head.moveLeft(); case SnakeDirection.right: head.moveRight(); case SnakeDirection.top: head.moveTop(); case SnakeDirection.bottom: head.moveBottom(); } return head; } bool canMove() { // compute head's next cell CellLocation head = nextHeadLocation(); // check head is inside board if (head.col < 0 || head.col > (boardSize - 1) || head.row < 0 || head.row > (boardSize - 1)) { return false; } // check head is not looped on snake body for (int i = 0; i < snake.cells.length - 1; i++) { if (head.col == snake.cells[i].col && head.row == snake.cells[i].row) { return false; } } return true; } void updateSnakeSize(int delta) { snake.size += delta; if (snake.size < 3) { snake.size = 3; } } void moveSnake() { if (!canMove()) { printlog('boom'); isFinished = true; return; } // compute head's next cell CellLocation head = nextHeadLocation(); // Append new head to snake cells snake.cells.add(head); // Drop tail while (snake.cells.length > snake.size) { snake.cells.removeAt(0); } computeBoard(); } void drawBoard() { String horizontalRule = '----'; for (int i = 0; i < board.cells[0].length; i++) { horizontalRule += '-'; } printlog('Board:'); printlog(horizontalRule); for (int rowIndex = 0; rowIndex < board.cells.length; rowIndex++) { String row = '| '; for (int colIndex = 0; colIndex < board.cells[rowIndex].length; colIndex++) { String cellValue = ' '; switch (board.getCellValue(CellLocation.go(rowIndex, colIndex))) { case CellValue.empty: cellValue = ' '; case CellValue.head: cellValue = 'O'; case CellValue.body: cellValue = 'o'; case CellValue.fruit: cellValue = 'X'; } row += cellValue; } row += ' |'; printlog(row); } printlog(horizontalRule); } void dump() { printlog(''); printlog('## Current game dump:'); printlog(''); printlog('$Activity:'); printlog(' Settings'); activitySettings.dump(); globalSettings.dump(); printlog(' State'); printlog(' isRunning: $isRunning'); printlog(' isStarted: $isStarted'); printlog(' isFinished: $isFinished'); printlog(' animationInProgress: $animationInProgress'); printlog(' Base data'); drawBoard(); printlog(' Game data'); printlog(' score: $score'); printlog(' gameWon: $gameWon'); printlog(''); } @override String toString() { return '$Activity(${toJson()})'; } Map<String, dynamic>? toJson() { return <String, dynamic>{ // Settings 'activitySettings': activitySettings.toJson(), 'globalSettings': globalSettings.toJson(), // State 'isRunning': isRunning, 'isStarted': isStarted, 'isFinished': isFinished, 'animationInProgress': animationInProgress, // Base data 'snake': snake.toJson(), 'board': board.toJson(), // Game data 'score': score, 'gameWon': gameWon, }; } }