Skip to content

sileneer/notebook-test-project

Repository files navigation

Tic-Tac-Toe Library

A comprehensive, extensible Tic-Tac-Toe library implementation in Kotlin with AI opponents and complete test coverage.

Features

  • ✅ All required features (1-5)
  • ✅ All bonus features (6-8)
  • ✅ Configurable player types (Human, Random AI, Smart AI)
  • ✅ Unbeatable AI using minimax algorithm
  • ✅ Comprehensive test suite (51 tests, 100% passing)
  • ✅ Kotlin Notebook demonstration
  • ✅ Complete KDoc documentation
  • ✅ Extensible architecture

Quick Start

Running the Application

./gradlew :app:run

Running Tests

./gradlew test

Project Structure

.
├── lib/                          # Core library
│   └── src/
│       ├── main/kotlin/
│       │   ├── TicTacToe.kt     # Game logic
│       │   └── Player.kt        # Player types
│       └── test/kotlin/
│           └── TicTacToeTests.kt # 51 comprehensive tests
├── app/                          # Command-line application
│   └── src/main/kotlin/
│       └── main.kt              # CLI implementation
├── TicTacToeDemo.ipynb          # Kotlin Notebook demo
├── IMPLEMENTATION.md            # Detailed implementation guide
└── README.md                    # This file

Using the Library

Basic Game

import org.jetbrains.kotlinx.tictactoe.*

// Create a new game
val game = Game()

// Make moves
game.makeMove(Position(0, 0)) // X at top-left
game.makeMove(Position(1, 1)) // O at center
game.makeMove(Position(0, 1)) // X

// Check game state
println(game.state.board)
println(game.state.result)

With Players

// Create different player types
val human = HumanPlayer("Alice", Mark.X) { gameState ->
    // Your input logic here
    Position(0, 0)
}

val randomAI = RandomComputerPlayer("Bot", Mark.O)
val smartAI = SmartComputerPlayer("DeepBlue", Mark.X)

// Use in game
val game = Game()
while (!game.state.isGameOver()) {
    val player = getCurrentPlayer()
    val move = player.chooseMove(game.state)
    game.makeMove(move)
}

AI Analysis

// Find best move for current position
val bestMove = Game.findBestMove(board, Mark.X)

// Evaluate a position
val score = Game.evaluatePosition(board, Mark.X, true)

Implementation Highlights

Architecture

  • Immutable design: Board and states are immutable for safety
  • Separation of concerns: Game logic completely independent of UI
  • Type safety: Leverages Kotlin's type system to prevent invalid states
  • Extensible: Easy to add new player types or UIs

AI Implementation

The Smart AI uses the minimax algorithm with optimizations:

  • Evaluates all possible game outcomes
  • Prefers faster wins and slower losses (depth-based scoring)
  • First-move heuristic for performance
  • Unbeatable when playing optimally

Testing

51 comprehensive tests covering:

  • Board operations and immutability
  • Game rules and win detection
  • All player types
  • Minimax algorithm correctness
  • Edge cases and error handling

Command-Line Interface

The CLI demonstrates all features:

  1. Player name input
  2. Player type selection (Human/Random AI/Smart AI)
  3. Multiple input formats (0-8 or row,col)
  4. Visual board display
  5. Error handling and validation
  6. Play again option

Kotlin Notebook

See TicTacToeDemo.ipynb for an interactive demonstration including:

  • Basic game creation
  • Board operations
  • Different player types
  • AI vs AI games
  • Minimax algorithm testing
  • Custom game scenarios

Documentation

All public APIs include comprehensive KDoc documentation:

  • Purpose and behavior
  • Parameters and return values
  • Exceptions
  • Usage examples

See IMPLEMENTATION.md for detailed design decisions and architecture discussion.

Building

Build All

./gradlew build

Build Library Only

./gradlew :lib:build

Build Application

./gradlew :app:build

Create Distribution

./gradlew :app:distZip

The distribution will be in app/build/distributions/.

Testing

Run All Tests

./gradlew test

Run with Coverage

./gradlew test jacocoTestReport

Test Results

All 51 tests pass:

  • 9 Board tests
  • 4 Position tests
  • 3 Mark tests
  • 14 Game tests
  • 10 Player tests
  • 6 Minimax tests
  • 3 GameResult tests
  • 2 Integration tests

Extending the Library

Adding a New Player Type

class MyPlayer(
    override val name: String,
    override val mark: Mark
) : Player {
    override fun chooseMove(gameState: GameState): Position {
        // Your logic here
    }
}

Adding a New Display Format

fun Board.toHTML(): String {
    // Generate HTML representation
}

Customizing Game Rules

class CustomGame : Game() {
    // Override methods to customize behavior
}

Design Decisions

Why Immutability?

  • Easier to reason about
  • Thread-safe by default
  • Enables functional style
  • Perfect for minimax algorithm

Why Minimax?

  • Optimal play guarantee
  • Well-understood algorithm
  • Educational value
  • Fast enough for Tic-Tac-Toe

Why Separate Player Interface?

  • UI-agnostic design
  • Easy to add new player types
  • Testable in isolation
  • Supports any input source

See IMPLEMENTATION.md for complete discussion of design trade-offs and alternatives considered.

Requirements Checklist

Required Features

  • ✅ Request names of Player X and Player O
  • ✅ Print initial board state
  • ✅ Players alternate choosing positions
  • ✅ Print updated board after each move
  • ✅ Print result when game is over

Bonus Features

  • ✅ Configurable player types (Human/AI)
  • ✅ Smart AI with intelligent decisions (minimax)
  • ✅ Kotlin Notebook demonstration

Evaluation Criteria

  • ✅ Code works (51/51 tests passing)
  • ✅ Code is readable (clear structure, meaningful names)
  • ✅ Code is documented (comprehensive KDoc)
  • ✅ Tests exist and pass (51 comprehensive tests)
  • ✅ Easy to extend (interface-based, modular design)

Original Task Description

Task Description:

Your task is to implement a Tic-Tac-Toe library that is able to drive a game of Tic-Tac-Toe, but leaves it up to users of the library to create the UI.

After you have implemented the library, you must use it to create a simple command line game of Tic-Tac-Toe.

It should have the following features:

  1. When starting the game, it should request the names of Player X and Player 0.

  2. It should print the initial state of the board when players have been named.

  3. Each player should then switch choosing a position on the board, placing their mark.

  4. The game should print the updated board after each player has placed a mark.

  5. When the game is over, the game should print the result of the game.

Bonus Features:

  1. Make the player type configurable, I.e., it should be possible to choose whether one or both of the players are a human or a computer. The computer can just place marks at random.

  2. Make the computer opponent a real AI, making intelligent decisions.

  3. Create a Kotlin Notebook that demonstrates how to use the library.

If you have any questions, please contact christian.melchior@jetbrains.com.

Evaluation Criteria:

  1. Does the code work?
  2. Is the code readable?
  3. Is the code documented?
  4. Are there tests and are they passing?
  5. How easy is it to extend the code with new player types or display formats?

It is more important to make the code readable, documented and tested than implementing new features.

Prepare to discuss the following topics:

  • Why does your implementation work?

  • Why did you choose your particular implementation? What alternatives did you consider?

  • What are the advantages and disadvantages of your particular implementation?

  • Why did you choose the approach to testing you did? What are the advantages and disadvantages of your particular approach?

It is perfectly fine to use Google, Stack Overflow, OpenAI, Cursor or any other resource to solve the problem, but you should be prepared to explain your approach to the interviewer.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 2

  •  
  •