C Tutorial

C Project: Tic-Tac-Toe

A 3x3 board in a 2D array. Place X and O, print the grid, and detect a winner or a draw.

What you will build

Tic-tac-toe is a 3 by 3 grid of characters. Empty cells are a space. Players write 'X' and'O'. After every move the program prints the board and checks rows, columns, and both diagonals for a winner. If the grid fills with no winner, it is a draw.

Interactive play would wait on scanf. This tutorial plays a fixed sequence of coordinates so the output is the same every compile. Open Try it in C at /c/try — gcc, not Python, not HTML, not C++.

A 3x3 char board

Declare char board[3][3]. Fill it with spaces. A print helper writes three rows with| between cells so the grid is readable in a console.

Example

#include <stdio.h>

void print_board(char b[3][3]) {
  for (int r = 0; r < 3; r++) {
    printf(" %c | %c | %c\n", b[r][0], b[r][1], b[r][2]);
    if (r < 2) {
      printf("---+---+---\n");
    }
  }
}

int main(void) {
  char board[3][3] = {
      {' ', ' ', ' '},
      {' ', ' ', ' '},
      {' ', ' ', ' '},
  };
  board[0][0] = 'X';
  board[1][1] = 'O';
  print_board(board);
  return 0;
}

Row comes first, then column. board[0][0] is the top-left cell. That order must stay consistent in the move list and in the winner check.

Winner check

A player wins with three of the same mark in a line. Check three rows, three columns, and two diagonals. Skip a line whose first cell is still a space so three empties are not a win. Return the winning character, or a space if nobody has won yet.

Example

#include <stdio.h>

char winner(char b[3][3]) {
  for (int i = 0; i < 3; i++) {
    if (b[i][0] != ' ' && b[i][0] == b[i][1] && b[i][1] == b[i][2]) {
      return b[i][0];
    }
    if (b[0][i] != ' ' && b[0][i] == b[1][i] && b[1][i] == b[2][i]) {
      return b[0][i];
    }
  }
  if (b[0][0] != ' ' && b[0][0] == b[1][1] && b[1][1] == b[2][2]) {
    return b[0][0];
  }
  if (b[0][2] != ' ' && b[0][2] == b[1][1] && b[1][1] == b[2][0]) {
    return b[0][2];
  }
  return ' ';
}

int main(void) {
  char board[3][3] = {
      {'X', 'O', ' '},
      {'X', 'O', ' '},
      {'X', ' ', ' '},
  };
  char w = winner(board);
  if (w == ' ') {
    printf("no winner yet\n");
  } else {
    printf("%c wins\n", w);
  }
  return 0;
}

The left column is three X marks, so this prints X wins. Compile it at/c/try. Use the C editor, not the Python, HTML, or C++ one.

Play a fixed sequence

Store moves as row and column pairs. X starts. Alternate marks. After each legal move, print the board and test for a winner. This sequence fills the left column with X and the middle column with two O marks. X wins on move 5. The program then stops.

Example

#include <stdio.h>

void print_board(char b[3][3]) {
  for (int r = 0; r < 3; r++) {
    printf(" %c | %c | %c\n", b[r][0], b[r][1], b[r][2]);
    if (r < 2) {
      printf("---+---+---\n");
    }
  }
  printf("\n");
}

char winner(char b[3][3]) {
  for (int i = 0; i < 3; i++) {
    if (b[i][0] != ' ' && b[i][0] == b[i][1] && b[i][1] == b[i][2]) {
      return b[i][0];
    }
    if (b[0][i] != ' ' && b[0][i] == b[1][i] && b[1][i] == b[2][i]) {
      return b[0][i];
    }
  }
  if (b[0][0] != ' ' && b[0][0] == b[1][1] && b[1][1] == b[2][2]) {
    return b[0][0];
  }
  if (b[0][2] != ' ' && b[0][2] == b[1][1] && b[1][1] == b[2][0]) {
    return b[0][2];
  }
  return ' ';
}

int main(void) {
  char board[3][3] = {
      {' ', ' ', ' '},
      {' ', ' ', ' '},
      {' ', ' ', ' '},
  };
  int rows[] = {0, 0, 1, 1, 2};
  int cols[] = {0, 1, 0, 1, 0};
  int n = 5;
  char mark = 'X';

  for (int m = 0; m < n; m++) {
    int r = rows[m];
    int c = cols[m];
    board[r][c] = mark;
    printf("move %d: %c at %d,%d\n", m + 1, mark, r, c);
    print_board(board);
    char w = winner(board);
    if (w != ' ') {
      printf("%c wins\n", w);
      return 0;
    }
    mark = (mark == 'X') ? 'O' : 'X';
  }
  printf("draw\n");
  return 0;
}

After five prints you should see X down the first column and the line X wins. If you change the last move to 2,2 instead of 2,0, the function should not declare a winner yet.

A scripted draw

Nine moves with no three-in-a-row should print draw. The board is full, winnerstill returns a space, and the loop ends. This second full program is a useful check that the diagonal tests do not fire on mixed marks.

Example

#include <stdio.h>

void print_board(char b[3][3]) {
  for (int r = 0; r < 3; r++) {
    printf(" %c | %c | %c\n", b[r][0], b[r][1], b[r][2]);
    if (r < 2) {
      printf("---+---+---\n");
    }
  }
  printf("\n");
}

char winner(char b[3][3]) {
  for (int i = 0; i < 3; i++) {
    if (b[i][0] != ' ' && b[i][0] == b[i][1] && b[i][1] == b[i][2]) {
      return b[i][0];
    }
    if (b[0][i] != ' ' && b[0][i] == b[1][i] && b[1][i] == b[2][i]) {
      return b[0][i];
    }
  }
  if (b[0][0] != ' ' && b[0][0] == b[1][1] && b[1][1] == b[2][2]) {
    return b[0][0];
  }
  if (b[0][2] != ' ' && b[0][2] == b[1][1] && b[1][1] == b[2][0]) {
    return b[0][2];
  }
  return ' ';
}

int main(void) {
  char board[3][3];
  for (int r = 0; r < 3; r++) {
    for (int c = 0; c < 3; c++) {
      board[r][c] = ' ';
    }
  }

  int rows[] = {0, 0, 0, 1, 1, 2, 1, 2, 2};
  int cols[] = {0, 1, 2, 2, 0, 0, 1, 2, 1};
  int n = 9;
  char mark = 'X';

  for (int m = 0; m < n; m++) {
    board[rows[m]][cols[m]] = mark;
    printf("move %d: %c at %d,%d\n", m + 1, mark, rows[m], cols[m]);
    print_board(board);
    char w = winner(board);
    if (w != ' ') {
      printf("%c wins\n", w);
      return 0;
    }
    mark = (mark == 'X') ? 'O' : 'X';
  }
  printf("draw\n");
  return 0;
}

Common mistakes

  • Treating three spaces as a win because ' ' == ' ' == ' '. Guard withb[i][0] != ' ' first.
  • Mixing row and column when you place a mark. Print the coordinates beside each move until the grid looks right.
  • Overwriting a filled cell. In a later interactive version, refuse a move when the cell is not a space.
  • Checking for a winner only at the end. Print and test after every move so a five-move win stops the game.

Practice

  1. Refuse a move into an occupied cell and print an error, then continue with the rest of the script.
  2. Write a sequence where O wins on a diagonal and confirm the board prints after each of the seven moves.
  3. Count empty cells and use that count to detect a draw without requiring the move list to have length 9.

FAQ: C Project: Tic-Tac-Toe

Common questions about this page.

What is the StudyGrid C tutorial?

The StudyGrid C tutorial is a full beginner-to-advanced track: syntax, types, input, loops, functions, pointers, structs, files, and the standard library. Each chapter has copy-and-run examples.

Should I run c tictactoe project examples locally for better learning?

Yes. Use the browser editor on StudyGrid for a quick check, then Download the example and run it on your computer. Local runs show real errors and the real toolchain, which is one of the fastest ways to learn c tictactoe project in this C C lesson (C Project: Tic-Tac-Toe).

Is the C editor the same as Try Python, Try HTML, or Try C++?

No. Try C compiles with gcc at /c/try and shows stdout plus compiler messages. Try Python stays at /try. Try HTML stays at /html/try. Try C++ stays at /cpp/try. C lessons never open those editors.

Do I need to install a compiler to learn C?

No. Open a chapter, click Try it in C, and compile in the browser. You can also download a .c file and compile locally with gcc.

Where should I start the C tutorial?

Start at C Intro, then Get Started and Syntax. After the first program, continue to output, variables, and if-else. After pointers, open C Examples, then files and bitwise. Use Next at the bottom of each chapter.

Is the C tutorial free?

Yes. The C workshop on StudyGrid (studygrid.in) is free: dashboard, chapters, and the compile-and-run editor.