Python Strings

Intro - Tic-Tac-Toe Example

Sources referenced: _notebooks/Foundation/F-projects/2025-08-13-tic-tac-toe-code-lesson.ipynb and 2025-08-18-tictactoe-code.ipynb (display and win-check logic).

Key ideas about Python str

String basics (literals, escaping, indexing, slicing)

Try the runnable examples in the next cell to see these concepts in action.

# String basics examples
a = 'hello'
b = "world"
print('a, b ->', a, b)

# multi-line and raw strings
multi = '''line1
line2'''
print('multi (repr) ->', repr(multi))
raw = r'C:\temp\newfile.txt'
print('raw ->', raw)

# indexing and slicing
print('a[0] ->', a[0])
print('a[-1] ->', a[-1])
print('a[1:4] ->', a[1:4])

# concatenation, repetition, and common methods
print('concat ->', a + ' ' + b)
print('repeat ->', a * 2)
print('upper ->', a.upper())
print('replace ->', a.replace('l','L'))
print('find ->', a.find('l'))
print('split ->', 'a,b,c'.split(','))
print('join ->', '-'.join(['a','b','c']))

# immutability demonstration
try:
    a[0] = 'H'
except TypeError as e:
    print('immutability error (expected):', e)

# formatting
name = 'Alice'; score = 42
print(f'{name} scored {score}')
print('format -> {}'.format(name))

# small exercise: build a one-line board string
board = [' ', 'X', 'O', ' ', 'X', ' ', 'O', ' ', ' ']
one_line = ''.join(board)
print('one_line ->', repr(one_line))
print('split back ->', list(one_line))
# 🔧 Mini-Challenge 1: Create and Explore the Board

# Create an empty board (9 empty spaces)
board = [" "] * 9

print("Our empty board list:") # Print a string
print(board)
print()

# Let's see what's at different positions
print("Position 0 (top-left):", repr(board[0]))
print("Position 4 (center):", repr(board[4]))  
print("Position 8 (bottom-right):", repr(board[8]))
print()

# Now let's place some symbols and see what happens
board[0] = "X"  # Top-left
board[4] = "O"  # Center  
board[8] = "X"  # Bottom-right

print("After placing some symbols:")
print(board)
Our empty board list:
[' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']

Position 0 (top-left): ' '
Position 4 (center): ' '
Position 8 (bottom-right): ' '

After placing some symbols:
['X', ' ', ' ', ' ', 'O', ' ', ' ', ' ', 'X']

Board as a list of strings

We represent the Tic-tac-toe board as board = [ ] * 9 where each square is a string (empty string or a single-character mark like 'X' or 'O'). The repository’s Tic-tac-toe lesson uses a print_board function; we include it here exactly as used in the lesson so you can copy/run it.

# print_board function taken from the Tic-tac-toe lesson notebook
def print_board(board):
    """This function takes a board list and displays it as tic-tac-toe"""
    print()  # Empty line for spacing
    print(f" {board[0]} | {board[1]} | {board[2]}")
    print("---+---+---")
    print(f" {board[3]} | {board[4]} | {board[5]}")
    print("---+---+---")
    print(f" {board[6]} | {board[7]} | {board[8]}")
    print()  # Empty line for spacing

# create a board and demonstrate placing moves
board = [' '] * 9
board[4] = 'X'   # place X in the center
board[0] = 'O'   # place O at top-left
print_board(board)

Useful string operations with game examples

We’ll demonstrate some common str operations and why they matter when handling board contents.

s = 'x'
print('original:', s)
print('upper ->', s.upper())
print('concat ->', s + ' and O')
print('repeat ->', s * 3)
# converting marks to a single string representation of the board
row = '|'.join(board[0:3])
print('row string ->', row)
# splitting back into list values (example)
parts = row.split('|')
print('parts ->', parts)

Checking wins (win patterns)

The Tic-tac-toe lesson uses a set of winning index patterns. The check below is adapted from the repository’s notebook and returns True when a player has three matching marks (non-empty) in any winning pattern.

win_patterns = [
    [0,1,2], [3,4,5], [6,7,8],  # rows
    [0,3,6], [1,4,7], [2,5,8],  # columns
    [0,4,8], [2,4,6]            # diagonals
]

def check_winner(board, symbol):
    """Return True if `symbol` (e.g. 'X') has a winning pattern on `board`.
    This uses the same idea shown in the Tic-tac-toe lesson notebooks.
    """
    for combo in win_patterns:
        if all(board[i] == symbol for i in combo):
            return True
    return False

# quick check
b = ['X','X','X',' ',' ',' ',' ',' ',' ']
print('check_winner ->', check_winner(b, 'X'))  # True

Finding a two-in-a-row (simple example)

To connect string handling to game heuristics, here is a small helper that finds an empty cell that would complete a player’s three-in-a-row (similar to patterns used in some Tic-tac-toe AI). This is a direct, minimal translation of the idea used in the JS implementation in the repository.

def find_best_move(board, player):
    """Return index of empty square that completes `player` to three-in-a-row, or -1 if none.
    """
    for pattern in win_patterns:
        line = [board[i] for i in pattern]
        player_count = sum(1 for cell in line if cell == player)
        empty_count = sum(1 for cell in line if cell == ' ')
        if player_count == 2 and empty_count == 1:
            empty_pos = pattern[line.index(' ')]
            return empty_pos
    return -1

# test the helper
test_board = ['X','X',' ',' ',' ',' ',' ',' ',' ']
print('find_best_move ->', find_best_move(test_board, 'X'))  # Expect 2
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

Cell In[4], line 15
     13 # test the helper
     14 test_board = ['X','X',' ',' ',' ',' ',' ',' ',' ']
---> 15 print('find_best_move ->', find_best_move(test_board, 'X'))  # Expect 2


Cell In[4], line 4, in find_best_move(board, player)
      1 def find_best_move(board, player):
      2     """Return index of empty square that completes `player` to three-in-a-row, or -1 if none.
      3     """
----> 4     for pattern in win_patterns:
      5         line = [board[i] for i in pattern]
      6         player_count = sum(1 for cell in line if cell == player)


NameError: name 'win_patterns' is not defined
# full small demonstration putting it together
board_demo = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
board_demo[0] = 'X'
board_demo[1] = 'X'
print_board(board_demo)
move = find_best_move(board_demo, 'X')
print('best move for X ->', move)
if move != -1:
    board_demo[move] = 'X'
    print_board(board_demo)
    print('X wins?', check_winner(board_demo, 'X'))
---------------------------------------------------------------------------

NameError                                 Traceback (most recent call last)

Cell In[3], line 5
      3 board_demo[0] = 'X'
      4 board_demo[1] = 'X'
----> 5 print_board(board_demo)
      6 move = find_best_move(board_demo, 'X')
      7 print('best move for X ->', move)


NameError: name 'print_board' is not defined

Notes and next steps

Beginner-friendly recap and helpers

Below are clearer, well-commented helper implementations for the key ideas shown in this lesson (win detection, finding a blocking/winning move, and safe move placement). These are written so beginners can read and understand every step — run the cell to make the helpers available and then run the quick checks that follow.

# Beginner-friendly helper implementations
win_patterns = [
    [0,1,2], [3,4,5], [6,7,8],  # rows
    [0,3,6], [1,4,7], [2,5,8],  # columns
    [0,4,8], [2,4,6]            # diagonals
]

def check_winner_simple(board, symbol):
    """Return True if `symbol` appears in any win pattern on `board`.
    This beginner-friendly version loops explicitly so new learners can follow the logic.
    """
    for combo in win_patterns:
        a, b, c = combo  # unpack the three indices
        # Check each position in the pattern contains the symbol
        if board[a] == symbol and board[b] == symbol and board[c] == symbol:
            return True
    return False

def find_best_move_simple(board, player):
    """Return index of empty square that completes `player` to three-in-a-row, or -1 if none.
    This version builds the line values and counts occurrences in a readable way.
    """
    for pattern in win_patterns:
        line = [board[i] for i in pattern]  # values at the three positions
        # Count how many of the player's marks and how many empty spaces
        player_count = sum(1 for cell in line if cell == player)
        empty_count = sum(1 for cell in line if cell == ' ')
        if player_count == 2 and empty_count == 1:
            # find which position in the pattern is empty and return its board index
            empty_pos_in_line = line.index(' ')
            return pattern[empty_pos_in_line]
    return -1

def make_move_simple(board, position, player):
    """Try to place player's mark at position (0-8). Return True if successful.
    This function validates the position and ensures we don't overwrite an occupied square.
    """
    # Validate position is an integer in the right range
    if not isinstance(position, int):
        return False
    if position < 0 or position > 8:
        return False
    # Check if the square is empty (represented by a single space ' ')
    if board[position] != ' ':
        return False
    # Place the player's symbol and return True
    board[position] = player
    return True

# Make these the default helpers for the rest of the notebook (simple names)
check_winner = check_winner_simple
find_best_move = find_best_move_simple
make_move = make_move_simple

print('Beginner-friendly helpers loaded: check_winner, find_best_move, make_move')
# Quick assertions to help beginners self-validate the functions
assert check_winner(['X','X','X',' ',' ',' ',' ',' ',' '], 'X') == True, 'check_winner failed for top row'
assert check_winner(['O',' ',' ','O',' ',' ','O',' ',' '], 'O') == True, 'check_winner failed for column'
assert check_winner(['X','O','X','O','X','O','O','X','O'], 'X') == False, 'check_winner false positive'

assert find_best_move(['X','X',' ',' ',' ',' ',' ',' ',' '], 'X') == 2, 'find_best_move should return 2'
assert find_best_move(['O',' ',' ','O',' ',' ','O',' ',' '], 'O') == 6, 'find_best_move should return 6'
assert find_best_move(['X','O','X','O','X','O','O','X','O'], 'X') == -1, 'find_best_move should return -1 when none'

b = [' ',' ',' ',' ',' ',' ',' ',' ',' ']
assert make_move(b, 4, 'X') == True and b[4] == 'X', 'make_move should place X at 4'
b2 = ['X',' ',' ',' ',' ',' ',' ',' ',' ']
assert make_move(b2, 0, 'O') == False and b2[0] == 'X', 'make_move should not overwrite'

print('All beginner checks passed — if you see this message you are ready to continue!')