forked from souravjain540/Basic-Python-Programs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathconnect_four.py
More file actions
170 lines (137 loc) · 5.1 KB
/
connect_four.py
File metadata and controls
170 lines (137 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#!/usr/bin/env python3
"""
Connect Four – a simple console implementation for one or two players.
The game uses a 6x7 board. Players take turns dropping a disc into one of the
seven columns. The disc occupies the lowest available row in that column.
The first player to connect four of their discs horizontally, vertically,
or diagonally wins. If the board fills without a winner, the game ends in a
draw.
Usage:
python connect_four.py
The program will prompt for the number of players (1 or 2). In single‑player
mode the second player is a very simple AI that chooses the first available
column. Feel free to replace the AI logic with something more sophisticated.
"""
from __future__ import annotations
import sys
import random
from typing import List, Tuple
ROWS: int = 6
COLUMNS: int = 7
EMPTY: str = " "
PLAYER_SYMBOLS: Tuple[str, str] = ("X", "O")
def create_board() -> List[List[str]]:
"""Return a new empty board."""
return [[EMPTY for _ in range(COLUMNS)] for _ in range(ROWS)]
def print_board(board: List[List[str]]) -> None:
"""Print the board to the console."""
print("\n" + " ".join(str(i + 1) for i in range(COLUMNS)))
for row in board:
print("|" + "|".join(row) + "|")
print("-" * (COLUMNS * 2 + 1))
def is_valid_move(board: List[List[str]], col: int) -> bool:
"""Return True if a disc can be dropped into the given column."""
return board[0][col] == EMPTY
def make_move(board: List[List[str]], col: int, symbol: str) -> None:
"""Drop a disc into the given column."""
for row in reversed(range(ROWS)):
if board[row][col] == EMPTY:
board[row][col] = symbol
break
def check_direction(
board: List[List[str]],
start_row: int,
start_col: int,
delta_row: int,
delta_col: int,
symbol: str,
) -> bool:
"""Check a line of four in a specific direction."""
count = 0
row, col = start_row, start_col
while 0 <= row < ROWS and 0 <= col < COLUMNS:
if board[row][col] == symbol:
count += 1
if count == 4:
return True
else:
count = 0
row += delta_row
col += delta_col
return False
def check_win(board: List[List[str]], symbol: str) -> bool:
"""Return True if the given symbol has a connect four."""
# Horizontal
for r in range(ROWS):
if check_direction(board, r, 0, 0, 1, symbol):
return True
# Vertical
for c in range(COLUMNS):
if check_direction(board, 0, c, 1, 0, symbol):
return True
# Diagonal /
for r in range(ROWS - 3):
for c in range(COLUMNS - 3):
if check_direction(board, r, c, 1, 1, symbol):
return True
# Diagonal \
for r in range(3, ROWS):
for c in range(COLUMNS - 3):
if check_direction(board, r, c, -1, 1, symbol):
return True
return False
def board_full(board: List[List[str]]) -> bool:
"""Return True if the board has no empty cells."""
return all(cell != EMPTY for cell in board[0])
def get_player_move(board: List[List[str]], player_num: int) -> int:
"""Prompt the human player for a column."""
while True:
try:
col = int(input(f"Player {player_num} ({PLAYER_SYMBOLS[player_num - 1]}), choose column (1-{COLUMNS}): ")) - 1
if 0 <= col < COLUMNS and is_valid_move(board, col):
return col
print(f"Column {col + 1} is full or out of range. Try again.")
except ValueError:
print("Invalid input. Enter a number.")
def get_ai_move(board: List[List[str]]) -> int:
"""Very simple AI: choose the first available column."""
for col in range(COLUMNS):
if is_valid_move(board, col):
return col
# Should never reach here if called correctly
raise RuntimeError("No valid moves for AI")
def main() -> None:
print("Welcome to Connect Four!")
while True:
try:
num_players = int(input("Enter number of players (1 or 2): "))
if num_players in (1, 2):
break
print("Please enter 1 or 2.")
except ValueError:
print("Invalid input. Enter a number.")
board = create_board()
current_player = 1
while True:
print_board(board)
if num_players == 1 and current_player == 2:
col = get_ai_move(board)
print(f"AI chooses column {col + 1}")
else:
col = get_player_move(board, current_player)
make_move(board, col, PLAYER_SYMBOLS[current_player - 1])
if check_win(board, PLAYER_SYMBOLS[current_player - 1]):
print_board(board)
if num_players == 1 and current_player == 2:
print("AI wins! Better luck next time.")
else:
print(f"Player {current_player} ({PLAYER_SYMBOLS[current_player - 1]}) wins!")
break
if board_full(board):
print_board(board)
print("It's a draw!")
break
current_player = 2 if current_player == 1 else 1
print("Thanks for playing!")
if __name__ == "__main__":
main()