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 thesim
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:
- Deploying the app
- Real-time multiplayer (You could use the techniques from The simplest way to build an instant messaging app with Django 🌮)
If you want to build another HTMX and Django app, check out: Create a quiz app with HTMX and Django in 8 mins ☑️
Build your Django frontend even faster?
I want to release high-quality products as soon as possible. Probably like you, I want to make my Django product ideas become reality as soon as possible.
That's why I built Photon Designer - an entirely visual editor for building Django frontend at the speed that light hits your eyes. Photon Designer outputs neat, clean