Skip to main content

Connect Four with local play and a networked server/clients.

Project description

Getting Started

A terminal Connect Four game in Python. This guide gets you playing locally and shows the mechanics of writing your own bot. (For playing over a network with a server and clients, see NETWORKED_PLAY.md.)


Requirements

  • Python 3.13 or newer — check with python3 --version.
  • No packages to install — it uses only the standard library.

Run all commands from the project folder (the one with local.py and the bot/ directory). If python3 isn't found, try python.


Play a local game

Local play runs everything in one program. Start a human-vs-human game:

python3 local.py

You'll enter a name, then see the board:

 0 1 2 3 4 5 6
+-+-+-+-+-+-+-+
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
| | | | | | | |
+-+-+-+-+-+-+-+

The numbers on top are column numbers, starting at 0. On your turn, type a column number and press Enter to drop your piece; it falls to the lowest open slot. Get win_length pieces in a row (horizontal, vertical, or diagonal) to win. Ctrl-C quits.

Choosing players

RED always moves first, then YELLOW. Use --red and --yellow to set each color to either human or a bot name:

python3 local.py                                    # human vs human (default)
python3 local.py --yellow leftmost                  # you (RED) vs leftmost bot
python3 local.py --red leftmost --yellow rightmost  # bot vs bot

The bots included so far are deliberately simple — they're your competition to beat:

Name Strategy
leftmost Plays the left-most column with room.
rightmost Plays the right-most column with room.
rand Plays a random column with room.
invalid Always plays an out-of-bounds column (for testing error handling).

Changing the board and win condition

Option Default Meaning
--cols 7 Board width
--rows 6 Board height
--win-length 4 Pieces in a row needed to win
# Same as the default:
python3 local.py --cols 7 --rows 6 --win-length 4

# A small, fast "three in a row" board — handy while testing a bot:
python3 local.py --cols 5 --rows 4 --win-length 3 --red leftmost --yellow rand

win_length must be at least 1 and can't exceed both the width and height (otherwise no win is possible); the game refuses impossible combinations.


Write your own bot

A bot is just one function that, given the current board and which piece you're playing, returns the column number to play. The rest of this section is the plumbing; the strategy is up to you.

1. Create a file in bot/

Add a file bot/<name>.py. Any file you drop in the bot/ folder is picked up automatically when the program starts (except files beginning with _ or test_). The name you register is the name you'll use on the command line.

Here is the entire leftmost bot (bot/leftmost.py) — the simplest complete example to model yours on:

from game import Board, Piece
from . import register


@register("leftmost")
def leftmost(board: Board, piece: Piece) -> int:
    for c in range(board.cols):
        if board.is_column_playable(c):
            return c
    raise ValueError("Board appears full")

The pieces that make it a bot:

  • from . import register — pulls in the registration helper from the bot package.
  • @register("leftmost") — registers the function under a name. Use a unique name for yours, e.g. @register("mybot").
  • The function signature must be (board: Board, piece: Piece) -> int.
  • It returns a column number (an int). It does not place the piece itself — the game does that.

2. What your function gets, and what it returns

  • board — the current position. Read it to decide your move (see the toolbox below). Treat it as read-only.
  • piecePiece.RED or Piece.YELLOW, whichever you are playing.
  • Return — the column number to drop into, 0 to board.cols - 1. Pick a column that still has room. If you return a full or out-of-range column the server counts it as an illegal move, so check first.

3. The board toolbox

These are the methods and values you'll use to inspect the position:

Tool What it gives you
board.cols, board.rows Board dimensions.
board.win_length Pieces in a row needed to win (may not be 4!).
board.is_column_playable(c) True if column c has room.
board.get_next_open_row(c) Row a new piece in column c would land on (None if full).
board[c][r] The Piece at column c, row r (Piece.EMPTY if empty). Row 0 is the bottom.
board.check_win(piece) True if piece already has a winning line.
board.is_full() True if no moves remain.
piece.opponent() The other player's piece.
board.copy() A separate copy you can experiment on.
board.move_piece(c, piece) Drops piece into column c on that board.

The last two are how you can try out a move without disturbing the real game: copy the board, call move_piece on the copy, and inspect the result (for example with check_win). The real board the game hands you is unchanged.

4. Run and test your bot

Once bot/mybot.py exists, use it like any other bot:

python3 local.py --red mybot --yellow leftmost
python3 local.py --red mybot --yellow rand --cols 5 --rows 4 --win-length 3

You can also call the function directly to test specific situations. Set up a position with move_piece, then assert your bot picks the column you expect:

import unittest
from game import Board, Piece
import bot


class TestMyBot(unittest.TestCase):
    def test_picks_a_playable_column(self):
        mybot = bot.strict_lookup("mybot")  # raises if "mybot" never registered
        board = Board()
        board.move_piece(3, Piece.RED)   # set up any position you like
        choice = mybot(board, Piece.YELLOW)
        self.assertTrue(board.is_column_playable(choice))


if __name__ == "__main__":
    unittest.main()

Run your tests with:

python3 -m unittest discover -p "test_*.py"

5. Now it's your turn

The example bots never look at where the pieces are — that's the bar to clear. Some questions to get you thinking (no code provided on purpose):

  • Can your bot notice when it has a move that wins right now, and take it?
  • Can it notice when the opponent is about to win, and block?
  • Which column is worth more when nothing urgent is happening?
  • How would you handle a board whose win_length isn't 4?

Start small, play it against leftmost and rand, and grow it from there.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jkh_c4-0.0.3.tar.gz (43.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jkh_c4-0.0.3-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

Details for the file jkh_c4-0.0.3.tar.gz.

File metadata

  • Download URL: jkh_c4-0.0.3.tar.gz
  • Upload date:
  • Size: 43.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jkh_c4-0.0.3.tar.gz
Algorithm Hash digest
SHA256 5bf468cfac267bd16f9e61f48d4f8c4b5cf7756a4e570d43aec620bddabcaf5f
MD5 9e90ea69c36131d50488b6a44d90fe5d
BLAKE2b-256 77a7b4c7c105ccb04496b6f06e3f20c6a015624c4bf9e4fee526ea6f550994db

See more details on using hashes here.

Provenance

The following attestation bundles were made for jkh_c4-0.0.3.tar.gz:

Publisher: release.yml on jkh52/c4

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file jkh_c4-0.0.3-py3-none-any.whl.

File metadata

  • Download URL: jkh_c4-0.0.3-py3-none-any.whl
  • Upload date:
  • Size: 28.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for jkh_c4-0.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 f32bd0e63972b14bd88331b85d4cc5a86945d6bad97caed4a3cdaac7dfcbca52
MD5 0307af6d686ace9b59595287246dd0f6
BLAKE2b-256 9e20d3a363c8b80641bdbfe940e47832eeead00446e867491ea951e2cf0b2a24

See more details on using hashes here.

Provenance

The following attestation bundles were made for jkh_c4-0.0.3-py3-none-any.whl:

Publisher: release.yml on jkh52/c4

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page