Build a Connect Four game with HTMX and Django in 8 minutes ๐ŸŸก๐Ÿ”ด

Published: April 11, 2024

We'll build a simple Connect4 game with Django and HTMX - fast ๐ŸŽ๏ธ

By the end, you'll have built a multiplayer game using HTMX, using neat server-side logic and storing all results in your database. HTMX is a great way to use javascript without writing javascript.

Here's how your final product will look ๐ŸŸก๐Ÿ”ด: Everything is rendered server-side, and the state is kept server-side.

Let's start ๐Ÿ‘จโ€๐Ÿš€

Setup your Django app

pip install --upgrade django
django-admin startproject core .
python manage.py startapp sim
  • Add our app sim to the INSTALLED_APPS in settings.py:
# settings.py
INSTALLED_APPS = [
'sim',
...
]

Add your game model with logic

  • Add the following code to sim/models.py:
from django.db import models
def default_board():
return [[0 for _ in range(6)] for _ in range(7)]
class Game(models.Model):
board = models.JSONField(default=default_board)
active_player = models.IntegerField(default=1)
winner = models.IntegerField(default=0)
def drop_piece(self, column) -> None:
for row in range(5, -1, -1):
if self.board[column][row] == 0:
self.board[column][row] = self.active_player
break
def check_winner(self) -> bool:
# Check horizontal
# I.e.,
#
# 1 1 1 1
#
for row in range(6):
for col in range(4):
if self.board[col][row] == self.active_player and
self.board[col + 1][row] == self.active_player and
self.board[col + 2][row] == self.active_player and
self.board[col + 3][row] == self.active_player:
self.winner = self.active_player
return True
# Check vertical
# I.e.,
# 1
# 1
# 1
# 1
for col in range(7):
for row in range(3):
if self.board[col][row] == self.active_player and
self.board[col][row + 1] == self.active_player and
self.board[col][row + 2] == self.active_player and
self.board[col][row + 3] == self.active_player:
self.winner = self.active_player
return True
# Check positive diagonal
# I.e.,
# 1
# 1
# 1
# 1
for col in range(4):
for row in range(3):
if self.board[col][row] == self.active_player and
self.board[col + 1][row + 1] == self.active_player and
self.board[col + 2][row + 2] == self.active_player and
self.board[col + 3][row + 3] == self.active_player:
self.winner = self.active_player
return True
# Check negative diagonal
# I.e.,
# 1
# 1
# 1
# 1
for col in range(4):
for row in range(5, 2, -1):
if self.board[col][row] == self.active_player and
self.board[col + 1][row - 1] == self.active_player and
self.board[col + 2][row - 2] == self.active_player and
self.board[col + 3][row - 3] == self.active_player:
self.winner = self.active_player
return True
# No winner, switch player
self.active_player = 1 if self.active_player == 2 else 2
return False
  • Make migrations and migrate the database:
python manage.py makemigrations
python manage.py migrate

Add your game views ( sim/views.py )

from django.shortcuts import render, redirect, reverse
from django.views.decorators.http import require_http_methods
from .models import Game
from django.http import HttpResponse
@require_http_methods(["GET", "POST"])
def index(request):
game, _ = Game.objects.get_or_create(id=1) # Ensure only one game is active
if request.method == 'POST':
column = int(request.POST.get('column'))
game.drop_piece(column)
if game.check_winner():
game.save()
return render(request, 'index.html', context={'game': game})
game.save()
context = {
'columns': range(7),
'rows': range(6),
'game': game
}
return render(request, 'index.html', context=context)
def reset(request):
game, _ = Game.objects.get_or_create(id=1)
game.board = [[0 for _ in range(6)] for _ in range(7)]
game.active_player = 1
game.winner = 0
game.save()
return redirect('index')

Add your HTML templates for the game

  • Create templates directory in the sim app

  • Create the game board by creating templates/index.html containing:

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Connect Four</title>
<script src="https://cdn.jsdelivr.net/npm/htmx.org"></script>
<style>
.container {
max-width: 70%;
margin: auto;
}
.container header {
text-align: center;
margin-bottom: 20px;
}
.token-container {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
}
.board {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 10px;
margin: auto;
/* centers the board horizontally */
min-height: 500px;
}
.row {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.cell {
display: flex;
justify-content: center;
align-items: center;
}
.reset-container {
padding-top: 20px;
display: flex;
justify-content: center;
}
</style>
</head>
<body>
<main class="container">
<header>
<h1>Connect Four ๐ŸŸก๐Ÿ”ด</h1>
</header>
{% if not game.winner %}
<div>
<p>It's player {{ game.active_player }}'s turn</p>
</div>
<form method="post" class="token-container" hx-boost="true">
{% csrf_token %} {% for column in columns %}
<button type="submit" name="column" value="{{ column }}">
Column {{ forloop.counter }}
</button>
{% endfor %}
</form>
{% endif %}
<section id="board-wrapper">
{% if game.winner %}
<h1>We have a winner ๐ŸŽ‰</h1>
<h2>Player {{ game.winner }} is the winner!</h2>
<a href="{% url 'reset' %}">Play again?</a>
{% else %}
<div class="board" id="board">
{% for row in game.board %}
<div class="row">
{% for cell in row %}
<span class="cell">
{% if cell == 1 %} ๐Ÿ”ด {% elif cell == 2 %} ๐ŸŸก {% else %} โฌœ๏ธ {%
endif %}
</span>
{% endfor %}
</div>
{% endfor %} {% endif %}
</div>
</section>
<div class="reset-container">
<a href="{% url 'reset' %}">Reset Game</a>
</div>
</main>
</body>
</html>

Update your urls

  • Update core/url.py to include our app:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('sim.urls')),
]
  • Create a file at sim/urls.py containing:
from django.urls import path
from sim import views
urlpatterns = [
path('', views.index, name='index'),
path('reset/', views.reset, name='reset'),
]

Run your server to play your game ๐ŸŸก๐Ÿ”ด

python manage.py runserver

Complete โœ…

Congrats. You've built a Connect Four game with Django and HTMX.

Now you might want to add more features like:

If you want to build another HTMX and Django app, check out: Create a quiz app with HTMX and Django in 8 mins โ˜‘๏ธ

P.S Want to ship better features with AI?
Join my free weekly newsletter

Each week, I share bite-sized learnings and AI news that matters - so you can build better software in practice.

No spam guaranteed ยท Unsubscribe whenever