diff --git a/android/gradle.properties b/android/gradle.properties
index bc2d95e8567abcfd41c26ebeb95fced48f43e773..818e87b23b224ced309ae5c147e5ed827826e237 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
 org.gradle.jvmargs=-Xmx1536M
 android.useAndroidX=true
 android.enableJetifier=true
-app.versionName=0.0.1
-app.versionCode=1
+app.versionName=0.0.2
+app.versionCode=2
diff --git a/fastlane/metadata/android/en-US/changelogs/2.txt b/fastlane/metadata/android/en-US/changelogs/2.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c4767c31cd60be21ad424c3e0b8357a7a045e229
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/2.txt
@@ -0,0 +1 @@
+Add minimal playable game.
diff --git a/fastlane/metadata/android/fr-FR/changelogs/2.txt b/fastlane/metadata/android/fr-FR/changelogs/2.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f787581174948c885580861f9718adf43a570a3a
--- /dev/null
+++ b/fastlane/metadata/android/fr-FR/changelogs/2.txt
@@ -0,0 +1 @@
+Ajout d'un jeu jouable minimal.
diff --git a/lib/cubit/game_cubit.dart b/lib/cubit/game_cubit.dart
index 9c748d0c4d7bb8aad974e18a4753d99be6fd4901..6f80a6134baa14c33d69a7561a3eae76e6b5df25 100644
--- a/lib/cubit/game_cubit.dart
+++ b/lib/cubit/game_cubit.dart
@@ -31,13 +31,13 @@ class GameCubit extends HydratedCubit<GameState> {
       isStarted: state.currentGame.isStarted,
       isFinished: state.currentGame.isFinished,
       animationInProgress: state.currentGame.animationInProgress,
-      boardAnimated: state.currentGame.boardAnimated,
       // Base data
       board: state.currentGame.board,
       // Game data
+      currentPlayer: state.currentGame.currentPlayer,
       scores: state.currentGame.scores,
     );
-    // game.dump();
+    game.dump();
 
     updateState(game);
   }
@@ -74,16 +74,84 @@ class GameCubit extends HydratedCubit<GameState> {
     refresh();
   }
 
-  // FIXME: should be removed
-  void doSomething() {
-    printlog('shoud do something!');
+  void toggleCurrentPlayer() {
+    state.currentGame.currentPlayer = 1 - state.currentGame.currentPlayer;
     refresh();
   }
 
-  // FIXME: should be removed
-  void logSomething([dynamic yolo]) {
-    printlog('logSomething: $yolo');
+  void tapOnCell(int cellIndex) {
+    printlog('tapOnCell: $cellIndex');
+
+    if (!state.currentGame.isCurrentPlayerHouse(cellIndex)) {
+      printlog('not allowed');
+
+      return;
+    }
+
+    if (state.currentGame.board.cells[cellIndex] == 0) {
+      printlog('empty cell');
+
+      return;
+    }
+
+    if (!state.currentGame.isMoveAllowed(cellIndex)) {
+      printlog('not allowed (need to give at least one seed to other player)');
+
+      return;
+    }
+
+    state.currentGame.animationInProgress = true;
+    refresh();
+
+    final int lastCellIndex = animateSeedsDistribution(cellIndex);
+    animateSeedsEarning(lastCellIndex);
+
+    toggleCurrentPlayer();
+
+    state.currentGame.animationInProgress = false;
+    refresh();
+  }
+
+  int animateSeedsDistribution(int sourceCellIndex) {
+    printlog('animateSeedsDistribution / sourceCellIndex: $sourceCellIndex');
+
+    final int seedsCount = state.currentGame.board.cells[sourceCellIndex];
+
+    // empty source cell
+    state.currentGame.board.cells[sourceCellIndex] = 0;
+    printlog('animateSeedsDistribution / empty source cell');
     refresh();
+
+    int cellIndex = sourceCellIndex;
+    for (int i = 0; i < seedsCount; i++) {
+      cellIndex = state.currentGame.getNextCellIndex(cellIndex, sourceCellIndex);
+      state.currentGame.board.cells[cellIndex] += 1;
+      refresh();
+    }
+
+    refresh();
+
+    return cellIndex;
+  }
+
+  void animateSeedsEarning(int lastCellIndex) {
+    printlog('animateSeedsEarning / lastCellIndex: $lastCellIndex');
+
+    if (state.currentGame.isOpponentHouse(lastCellIndex)) {
+      final int seedsCount = state.currentGame.board.cells[lastCellIndex];
+      printlog('found $seedsCount seed(s) on final house.');
+
+      if ([2, 3].contains(seedsCount)) {
+        printlog('ok will earn these seeds.');
+
+        state.currentGame.board.cells[lastCellIndex] = 0;
+        state.currentGame.scores[state.currentGame.currentPlayer] += seedsCount;
+        refresh();
+
+        // (recursively) check previous cells
+        animateSeedsEarning(state.currentGame.getPreviousCellIndex(lastCellIndex));
+      }
+    }
   }
 
   @override
diff --git a/lib/models/game/board.dart b/lib/models/game/board.dart
index 33a684d9dc3faec8c2264cf90c480c5f5b8cf456..4889074e48fdc5b62c9f0231b03b80164cad8240 100644
--- a/lib/models/game/board.dart
+++ b/lib/models/game/board.dart
@@ -25,8 +25,21 @@ class Board {
 
   void dump() {
     printlog('');
-    printlog('$Board:');
-    printlog('  cells: $cells');
+    printlog('  $Board:');
+
+    const List<List<int>> indexes = [
+      [11, 10, 9, 8, 7, 6],
+      [0, 1, 2, 3, 4, 5],
+    ];
+
+    for (List<int> line in indexes) {
+      String row = '    ';
+      for (int index in line) {
+        row += '[${cells[index].toString().padLeft(2, ' ')}]';
+      }
+      printlog(row);
+    }
+
     printlog('');
   }
 
diff --git a/lib/models/game/game.dart b/lib/models/game/game.dart
index 786f95c825345317a1b5cf284b534a9105a11aba..df87774498bd92650494ef70cdbb33cafab85353 100644
--- a/lib/models/game/game.dart
+++ b/lib/models/game/game.dart
@@ -3,14 +3,6 @@ import 'package:awale/models/settings/settings_game.dart';
 import 'package:awale/models/settings/settings_global.dart';
 import 'package:awale/utils/tools.dart';
 
-typedef MovingTile = String;
-typedef Move = Board;
-typedef Player = String;
-typedef ConflictsCount = List<List<int>>;
-typedef AnimatedBoard = List<List<bool>>;
-typedef AnimatedBoardSequence = List<AnimatedBoard>;
-typedef Word = String;
-
 class Game {
   Game({
     // Settings
@@ -22,12 +14,12 @@ class Game {
     this.isStarted = false,
     this.isFinished = false,
     this.animationInProgress = false,
-    this.boardAnimated = const [],
 
     // Base data
     required this.board,
 
     // Game data
+    required this.currentPlayer,
     required this.scores,
   });
 
@@ -40,12 +32,12 @@ class Game {
   bool isStarted;
   bool isFinished;
   bool animationInProgress;
-  AnimatedBoard boardAnimated;
 
   // Base data
   final Board board;
 
   // Game data
+  int currentPlayer;
   List<int> scores;
 
   factory Game.createNull() {
@@ -56,6 +48,7 @@ class Game {
       // Base data
       board: Board.createNull(),
       // Game data
+      currentPlayer: 0,
       scores: [0, 0],
     );
   }
@@ -80,16 +73,57 @@ class Game {
       globalSettings: newGlobalSettings,
       // State
       isRunning: true,
-      boardAnimated: [],
       // Base data
       board: Board.createNew(cells: cells),
       // Game data
+      currentPlayer: 0,
       scores: [0, 0],
     );
   }
 
   bool get canBeResumed => isStarted && !isFinished;
 
+  int getNextCellIndex(int cellIndex, int firstCellIndex) {
+    final int nextCellIndex = (cellIndex + 1) % board.cells.length;
+
+    if (nextCellIndex == firstCellIndex) {
+      return getNextCellIndex(nextCellIndex, firstCellIndex);
+    }
+
+    return nextCellIndex;
+  }
+
+  int getPreviousCellIndex(int cellIndex) {
+    return (cellIndex - 1) % board.cells.length;
+  }
+
+  bool isCurrentPlayerHouse(int cellIndex) {
+    const allowedCellIndexes = [
+      [0, 1, 2, 3, 4, 5],
+      [6, 7, 8, 9, 10, 11],
+    ];
+    return allowedCellIndexes[currentPlayer].contains(cellIndex);
+  }
+
+  bool isOpponentHouse(int cellIndex) {
+    return !isCurrentPlayerHouse(cellIndex);
+  }
+
+  // Ensure move is allowed, from cell seeds count
+  bool isMoveAllowed(int cellIndex) {
+    final int seedsCount = board.cells[cellIndex];
+
+    int finalCellIndex = cellIndex;
+    for (int i = 0; i < seedsCount; i++) {
+      finalCellIndex = getNextCellIndex(finalCellIndex, cellIndex);
+      if (isOpponentHouse(finalCellIndex)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
   void dump() {
     printlog('');
     printlog('## Current game dump:');
@@ -103,7 +137,6 @@ class Game {
     printlog('    isStarted: $isStarted');
     printlog('    isFinished: $isFinished');
     printlog('    animationInProgress: $animationInProgress');
-    printlog('board:');
     board.dump();
     printlog('  Game data');
     printlog('    scores: $scores');
@@ -125,7 +158,6 @@ class Game {
       'isStarted': isStarted,
       'isFinished': isFinished,
       'animationInProgress': animationInProgress,
-      'boardAnimated': boardAnimated,
       // Base data
       'board': board.toJson(),
       // Game data
diff --git a/lib/ui/layouts/game_layout.dart b/lib/ui/layouts/game_layout.dart
index 7a89189b91864e569f5ed09217c5204db9eee771..2c89418c505ac019dc54658082ce99d2e62d5bcc 100644
--- a/lib/ui/layouts/game_layout.dart
+++ b/lib/ui/layouts/game_layout.dart
@@ -4,9 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:awale/cubit/game_cubit.dart';
 import 'package:awale/models/game/game.dart';
 import 'package:awale/ui/widgets/game/game_board.dart';
-import 'package:awale/ui/widgets/game/game_bottom.dart';
 import 'package:awale/ui/widgets/game/game_end.dart';
-import 'package:awale/ui/widgets/game/game_top.dart';
 
 class GameLayout extends StatelessWidget {
   const GameLayout({super.key});
@@ -24,11 +22,9 @@ class GameLayout extends StatelessWidget {
             mainAxisAlignment: MainAxisAlignment.start,
             crossAxisAlignment: CrossAxisAlignment.center,
             children: [
-              const GameTopWidget(),
               const SizedBox(height: 8),
               const GameBoardWidget(),
               const SizedBox(height: 8),
-              const GameBottomWidget(),
               const Expanded(child: SizedBox.shrink()),
               currentGame.isFinished ? const GameEndWidget() : const SizedBox.shrink(),
             ],
diff --git a/lib/ui/widgets/game/game_board.dart b/lib/ui/widgets/game/game_board.dart
index a519bbaf328b8ab2dae66b0a04fcea56907d644d..91715a8c4469e6c7599b94338562e2e990b65d54 100644
--- a/lib/ui/widgets/game/game_board.dart
+++ b/lib/ui/widgets/game/game_board.dart
@@ -3,6 +3,9 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 
 import 'package:awale/cubit/game_cubit.dart';
 import 'package:awale/models/game/game.dart';
+import 'package:awale/ui/widgets/game/game_house.dart';
+import 'package:awale/ui/widgets/game/game_player.dart';
+import 'package:awale/ui/widgets/game/game_score.dart';
 
 class GameBoardWidget extends StatelessWidget {
   const GameBoardWidget({super.key});
@@ -13,9 +16,89 @@ class GameBoardWidget extends StatelessWidget {
       child: BlocBuilder<GameCubit, GameState>(
         builder: (BuildContext context, GameState gameState) {
           final Game currentGame = gameState.currentGame;
+          final Color borderColor = Theme.of(context).colorScheme.onSurface;
 
-          // FIXME: should be implemented
-          return Text(currentGame.toString());
+          Widget getHouseContent(int cellIndex) {
+            final bool isTapAllowed = currentGame.isCurrentPlayerHouse(cellIndex);
+
+            return GestureDetector(
+              onTap: () {
+                if (isTapAllowed && !currentGame.animationInProgress) {
+                  BlocProvider.of<GameCubit>(context).tapOnCell(cellIndex);
+                }
+              },
+              child: GameHouseWidget(
+                seedsCount: currentGame.board.cells[cellIndex],
+                active: isTapAllowed,
+              ),
+            );
+          }
+
+          return Row(
+            mainAxisAlignment: MainAxisAlignment.spaceBetween,
+            crossAxisAlignment: CrossAxisAlignment.center,
+            children: [
+              GamePlayerWidget(
+                active: currentGame.currentPlayer == 0,
+              ),
+              Container(
+                margin: const EdgeInsets.all(2),
+                padding: const EdgeInsets.all(2),
+                decoration: BoxDecoration(
+                  color: borderColor,
+                  borderRadius: BorderRadius.circular(2),
+                  border: Border.all(
+                    color: borderColor,
+                    width: 2,
+                  ),
+                ),
+                child: Column(
+                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
+                  crossAxisAlignment: CrossAxisAlignment.center,
+                  children: [
+                    GameScoreWidget(
+                      score: currentGame.scores[0],
+                    ),
+                    Table(
+                      defaultColumnWidth: const IntrinsicColumnWidth(),
+                      children: [
+                        TableRow(children: [
+                          getHouseContent(0),
+                          getHouseContent(11),
+                        ]),
+                        TableRow(children: [
+                          getHouseContent(1),
+                          getHouseContent(10),
+                        ]),
+                        TableRow(children: [
+                          getHouseContent(2),
+                          getHouseContent(9),
+                        ]),
+                        TableRow(children: [
+                          getHouseContent(3),
+                          getHouseContent(8),
+                        ]),
+                        TableRow(children: [
+                          getHouseContent(4),
+                          getHouseContent(7),
+                        ]),
+                        TableRow(children: [
+                          getHouseContent(5),
+                          getHouseContent(6),
+                        ]),
+                      ],
+                    ),
+                    GameScoreWidget(
+                      score: currentGame.scores[1],
+                    )
+                  ],
+                ),
+              ),
+              GamePlayerWidget(
+                active: currentGame.currentPlayer == 1,
+              ),
+            ],
+          );
         },
       ),
     );
diff --git a/lib/ui/widgets/game/game_bottom.dart b/lib/ui/widgets/game/game_bottom.dart
deleted file mode 100644
index 21b062bfa07fceb9fd67043f1439d53924a124b3..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/game/game_bottom.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:awale/cubit/game_cubit.dart';
-import 'package:awale/models/game/game.dart';
-
-class GameBottomWidget extends StatelessWidget {
-  const GameBottomWidget({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<GameCubit, GameState>(
-      builder: (BuildContext context, GameState gameState) {
-        final Game currentGame = gameState.currentGame;
-
-        // FIXME: should be implemented
-        return Text(currentGame.board.toString());
-      },
-    );
-  }
-}
diff --git a/lib/ui/widgets/game/game_house.dart b/lib/ui/widgets/game/game_house.dart
new file mode 100644
index 0000000000000000000000000000000000000000..687b5f0ede88f34258a79b2079b01cd7221e6059
--- /dev/null
+++ b/lib/ui/widgets/game/game_house.dart
@@ -0,0 +1,45 @@
+import 'package:flutter/material.dart';
+
+class GameHouseWidget extends StatelessWidget {
+  const GameHouseWidget({
+    super.key,
+    required this.seedsCount,
+    required this.active,
+  });
+
+  final int seedsCount;
+  final bool active;
+
+  @override
+  Widget build(BuildContext context) {
+    final Color borderColor = active
+        ? Theme.of(context).colorScheme.onSecondary
+        : Theme.of(context).colorScheme.onTertiary;
+
+    return AspectRatio(
+      aspectRatio: 1,
+      child: Container(
+        margin: const EdgeInsets.all(2),
+        padding: const EdgeInsets.all(2),
+        decoration: BoxDecoration(
+          color: borderColor,
+          borderRadius: BorderRadius.circular(2),
+          border: Border.all(
+            color: borderColor,
+            width: 2,
+          ),
+        ),
+        width: 50,
+        child: Text(
+          seedsCount.toString(),
+          textAlign: TextAlign.center,
+          style: TextStyle(
+            fontSize: 30,
+            fontWeight: FontWeight.bold,
+            color: Theme.of(context).colorScheme.primary,
+          ),
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/game_player.dart b/lib/ui/widgets/game/game_player.dart
new file mode 100644
index 0000000000000000000000000000000000000000..fa1f9dc766765f63bf8215b6e5fc770fe04ae32d
--- /dev/null
+++ b/lib/ui/widgets/game/game_player.dart
@@ -0,0 +1,31 @@
+import 'package:flutter/material.dart';
+
+class GamePlayerWidget extends StatelessWidget {
+  const GamePlayerWidget({
+    super.key,
+    required this.active,
+  });
+
+  final bool active;
+
+  @override
+  Widget build(BuildContext context) {
+    final Color baseColor = active ? Colors.pink : Theme.of(context).colorScheme.surface;
+
+    return Container(
+      margin: const EdgeInsets.all(2),
+      padding: const EdgeInsets.all(2),
+      decoration: BoxDecoration(
+        color: baseColor,
+        borderRadius: BorderRadius.circular(2),
+        border: Border.all(
+          color: baseColor,
+          width: 2,
+        ),
+      ),
+      width: 100,
+      height: 100,
+      child: const SizedBox.shrink(),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/game_score.dart b/lib/ui/widgets/game/game_score.dart
new file mode 100644
index 0000000000000000000000000000000000000000..b7a21b79479b515402b1e0b1f42ab5af9fc9f31c
--- /dev/null
+++ b/lib/ui/widgets/game/game_score.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/material.dart';
+
+class GameScoreWidget extends StatelessWidget {
+  const GameScoreWidget({
+    super.key,
+    required this.score,
+  });
+
+  final int score;
+
+  @override
+  Widget build(BuildContext context) {
+    final Color borderColor = Theme.of(context).colorScheme.onPrimary;
+
+    return Container(
+      margin: const EdgeInsets.all(2),
+      padding: const EdgeInsets.all(2),
+      decoration: BoxDecoration(
+        color: borderColor,
+        borderRadius: BorderRadius.circular(2),
+        border: Border.all(
+          color: borderColor,
+          width: 2,
+        ),
+      ),
+      width: 100,
+      child: Text(
+        score.toString(),
+        textAlign: TextAlign.center,
+        style: TextStyle(
+          fontSize: 50,
+          fontWeight: FontWeight.bold,
+          color: Theme.of(context).colorScheme.primary,
+        ),
+      ),
+    );
+  }
+}
diff --git a/lib/ui/widgets/game/game_top.dart b/lib/ui/widgets/game/game_top.dart
deleted file mode 100644
index 9db74b2c8ca5160d547d889cfd36b84d5c02a950..0000000000000000000000000000000000000000
--- a/lib/ui/widgets/game/game_top.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-import 'package:flutter/material.dart';
-import 'package:flutter_bloc/flutter_bloc.dart';
-
-import 'package:awale/cubit/game_cubit.dart';
-import 'package:awale/models/game/game.dart';
-
-class GameTopWidget extends StatelessWidget {
-  const GameTopWidget({super.key});
-
-  @override
-  Widget build(BuildContext context) {
-    return BlocBuilder<GameCubit, GameState>(
-      builder: (BuildContext context, GameState gameState) {
-        final Game currentGame = gameState.currentGame;
-
-        // FIXME: should be implemented
-        return Text(currentGame.scores.toString());
-      },
-    );
-  }
-}
diff --git a/pubspec.yaml b/pubspec.yaml
index 0e92686f6327f64e7d7159bea93081bb3633b3f1..7d63e14c0b6da81ef06c0a2a39d14a1d7e19ac7a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@ description: Awale game
 
 publish_to: "none"
 
-version: 0.0.1+1
+version: 0.0.2+2
 
 environment:
   sdk: "^3.0.0"