Building A 2-Player Tic-tac-toe Game in Dart Console

Building A 2-Player Tic-tac-toe Game in Dart Console

Tic-tac-toe, a famous game from childhood was given to me by my facilitator, Ms. Sophia A. Abubakar as a project. She insisted that my colleagues and I build this game in such a way it works on the dart console. As always, fear and tension built up immediately after she gave us this task on everyone's end and I was not exempted. I started my research using different techniques to achieve this task and below, is what I finally came up with.

Task Overview

  • Build a 2-player Tic Tac Toe game (Version 1)
  • Write an article detailing your steps and thought process
  • Features of the game:
  • Draw out the tic tac toe board on the console
  • Handle user input for x and o and store it (For e.g, When a player 1 who is X wants to place an X on the screen, they can’t click the terminal. So you are going to replace this clicking by asking the user for a coordinate of where they want to place their piece.)

Features of The Game

  • The game is played on a grid that's 3 squares by 3 squares.
  • You are X, your friend (or the computer) is O. Players take turns putting their marks in empty squares.
  • The first player to get 3 of their marks in a row (up, down, across, or diagonally) is the winner.
  • When all 9 squares are full, the game is over and it is a draw.

Steps in Achieving this Task

  1. Firstly, I imported the following packages from dart, they include: dart:io and dart:core. The dart:core library provides basic collections, such as List, Map, and Set while dart:io library allows one to work with files, directories, sockets, processes, HTTP servers and clients, and more.

  2. I created a boolean and set it to false because at the start of the game, no player has won. Then I created another boolean and defined it as isXturn and set it as true. This boolean initiates whose turn it is to play.

  3. I created a list of numbers from 1 to 9 and made possible combinations called co-ordinates that if any of these co-ordinates are played, it would decide that a player has won.

  4. The board can be created using dash(_ _) and lines (| |), values from the arrays are then passed into each box.

  5. We create two variables called generateBoard and getnextCharacter. The first variable is used to create a function we would use to create our board in num 4 above, void generateBoard(). The second variable is used to get the user input of either X or O where we introduce the ternary operator ?, the shorthand for an if/else statement. This statement would ensure that when it is X turn, X will display and vice versa for O.

  6. We create an int variable called int.parse(stdin.readLineSync) from the dart:io library to convert the number the player would input as string X or O.

  7. We use isXturn = !isXturn; to change player's turn and use cordinates[number - 1] = isXturn ? 'X' : 'O'; to change each player's co-ordinates. Normally, for a player to win, he or she must play 3 or at most 6 turns but in a case where if (movementCount == 9), it would be a draw. We will initiate the clearScreen(); to clear the screen if a draw occurs.

  8. We create another variable called checkWinner where the For loop is used to ascertain the winner at every move of each player and if winner==false (means no winner) and it breaks. However, there is a recursive function, a function that calls itself if (winner == false) getnextCharacter();

  9. The game can be run in Mac OS or Windows depending on the type of Process used. Process is a thread that runs the application. We create an if else statement that tells the console to run the app if it is windows or Mac OS.

Note: This is a summary of the game itself. Below is the code snippet that can aid easy understanding of each step.

import 'dart:io';
import 'dart:core';

bool winner = false;
bool isXturn = true;

List<String> cordinates = ['1', '2', '3', '4', '5', '6', '7', '8', '9'];
List<String> combinations = ['012', '048', '036', '147', '246', '258', '345', '678'];
void main() {
  generateBoard();
  getnextCharacter();
}
int movementCount = 0;
//check if a combination is true or false for a player (X or 0)
bool checkCombination(String combination, String checkFor) {
  //split the numbers in a list of integers
  List<int> numbers = combination.split('').map((item) {
    return int.parse(item);
  }).toList();
  bool match = false;
  for (final item in numbers) {
    if (cordinates[item] == checkFor) {
      match = true;
    } else {
      match = false;
      break;
    }
  }
  return match;
}
void checkWinner(player) {
  for (final item in combinations) {
    bool combinationValidity = checkCombination(item, player);
    if (combinationValidity == true) {
      print('$player WON!');
      winner = true;
      break;
    }
  }
}
//get input, check winners
void getnextCharacter() {
  //get input from player
  print('Choose Number for ${isXturn == true ? "X" : "O"}');
  int number = int.parse(stdin.readLineSync()!);
  //change the value of selected number in cordinates
  cordinates[number - 1] = isXturn ? 'X' : 'O';
  //change player turn
  isXturn = !isXturn;
  //increment move count
  movementCount++;
  if (movementCount == 9) {
    winner = true;
    print('DRAW!');
  } else {

    //clear the console
    clearScreen();

    //redraw the board with the new information
    generateBoard();
  }
  //
  //Check Validity for players, declare winner
  //
  //check validity for player X
  checkWinner('X');
  //check validity for player O
  checkWinner('O');

  //until we have a winner, we call current function again
  if (winner == false) getnextCharacter();
}
//clear console screen
void clearScreen() {
  if (Platform.isWindows) {
    print(Process.runSync("cls", [], runInShell: true).stdout);
  } else {
    print(Process.runSync("clear", [], runInShell: true).stdout);
  }
}
//show current state of board
void generateBoard() {
  print('   |   |   ');
  print(' ${cordinates[0]} | ${cordinates[1]} | ${cordinates[2]} ');
  print('___|___|___');
  print('   |   |   ');
  print(' ${cordinates[3]} | ${cordinates[4]} | ${cordinates[5]} ');
  print('___|___|___');
  print('   |   |   ');
  print(' ${cordinates[6]} | ${cordinates[7]} | ${cordinates[8]} ');
  print('   |   |   ');

}