File size: 8,848 Bytes
9df07fa | 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 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | """
abc_model.py - ABC Model with Displacement Matrices (CORRECTED FOR KAPPA)
KEY INSIGHT:
- B_i represents the DISPLACEMENT from current MB position to new position
- If MB is at (x₁, y₁) and we place piece at (x₂, y₂):
- Δx = x₂ - x₁
- Δy = y₂ - y₁
- B has right column (Δx, Δy, 1)ᵀ
Example:
- Stage 0: MB at (0, 0, 1)
- Stage 1: Place at (-1, -1) → Δ = (-1-0, -1-0) = (-1, -1)
- B₁ right column: (-1, -1, 1)ᵀ
- Stage 2: Place at (0, 0) → Δ = (0-(-1), 0-(-1)) = (1, 1)
- B₂ right column: (1, 1, 1)ᵀ ✓
Author: Chaim Duchovny
Date: January 25, 2026 (FIXED FOR KAPPA SUPPORT)
"""
import numpy as np
class ABCModelDynamic:
"""ABC Model with displacement-based B matrices."""
def __init__(self, n: int = 2, t: float = 1.0, T: float = 1.41):
"""
Initialize for 2D board.
Args:
n: Dimension (2 for 2D board)
t: Minimum distance threshold for adjacency
T: Maximum distance threshold for adjacency
"""
self.n = n
self.t = t
self.T = T
self.c0 = np.array([0.0, 0.0, 1.0])
self.B_blocks = [] # Displacement matrices
self.piece_positions = [] # Actual positions on SB
self.a_product = np.eye(3) # Accumulated product
self.delta = {} # Delta values for each piece
self.kappa = {} # Tokenized vectors for each piece (ADDED)
self.stage = 0
# MB position tracking
self.MB_current = self.c0.copy() # Current MB center
self.MB_previous = self.c0.copy() # Previous MB center
self.history = []
def create_B_displacement(self, dx: float, dy: float) -> np.ndarray:
"""
Create displacement matrix B.
Moves MB by (dx, dy) displacement.
B · current_MB = new_MB
B = [1 0 dx]
[0 1 dy]
[0 0 1]
Right column: (dx, dy, 1)ᵀ
"""
B = np.array([
[1.0, 0.0, dx],
[0.0, 1.0, dy],
[0.0, 0.0, 1.0]
])
return B
def add_piece(self, position: tuple, delta_values: tuple, kappa_vector: np.ndarray = None):
"""
Add a piece at position with delta values and optional kappa vector.
Args:
position: (x, y) tuple
delta_values: (Δ₁, Δ₂, Δ₃) tuple
kappa_vector: Tokenized feature vector (optional, defaults to delta_values)
"""
x, y = position
# Current MB position
current_MB = self.MB_current.copy()
# Calculate displacement
dx = x - current_MB[0]
dy = y - current_MB[1]
# Create displacement matrix
B_i = self.create_B_displacement(dx, dy)
# Store
idx = len(self.B_blocks)
self.B_blocks.append(B_i)
self.piece_positions.append((x, y))
# Update accumulated product: a_t = a_{t-1} · B_t
self.a_product = self.a_product @ B_i
# Track MB positions
self.MB_previous = self.MB_current.copy()
self.MB_current = self.a_product @ self.c0 # Should equal (x, y, 1)
# Store differential vector
self.delta[idx] = delta_values
# Store tokenized vector (or use delta_values as default)
if kappa_vector is None:
kappa_vector = np.array(delta_values)
self.kappa[idx] = kappa_vector
# Increment stage
self.stage += 1
# Record move
self.history.append({
'stage': self.stage,
'SB_position': (x, y),
'displacement': (dx, dy),
'B_i': B_i.copy(),
'a_t': self.a_product.copy(),
'MB_prev': self.MB_previous.copy(),
'MB_curr': self.MB_current.copy(),
'delta': delta_values,
'kappa': kappa_vector.copy()
})
def add_move(self, x: float, y: float, player: int):
"""
Convenience method: Add a move with automatic delta values.
Args:
x, y: Position coordinates
player: 1 for X (Δ₂=1.0), 2 for O (Δ₂=1.1)
"""
delta_1 = 1.0 # Occupied
delta_2 = 1.0 if player == 1 else 1.1 # Player color
delta_3 = 1.0 # Same value for all pieces
self.add_piece((x, y), (delta_1, delta_2, delta_3))
def get_board_state(self) -> str:
"""Visual board state."""
pos_map = {
(-1, 1): 0, (0, 1): 1, (1, 1): 2,
(-1, 0): 3, (0, 0): 4, (1, 0): 5,
(-1, -1): 6, (0, -1): 7, (1, -1): 8
}
board = [' '] * 9
for i, pos in enumerate(self.piece_positions):
if pos in pos_map:
player = 'X' if self.delta[i][1] == 1.0 else 'O'
board[pos_map[pos]] = player
s = f" {board[0]} | {board[1]} | {board[2]} \n"
s += "---|---|---\n"
s += f" {board[3]} | {board[4]} | {board[5]} \n"
s += "---|---|---\n"
s += f" {board[6]} | {board[7]} | {board[8]} \n"
return s
def describe_move(self, stage: int):
"""Describe a specific move."""
if stage < 1 or stage > len(self.history):
return "Invalid stage"
move = self.history[stage - 1]
desc = "=" * 70 + "\n"
desc += f"MOVE {stage}: {move['SB_position']}\n"
desc += "=" * 70 + "\n\n"
idx = stage - 1
B_i = self.B_blocks[idx]
desc += f"Mobile Board (MB) movement:\n"
desc += f" Previous MB: {move['MB_prev']}\n"
desc += f" Target (SB): {move['SB_position']}\n"
desc += f" Displacement: Δ = {move['displacement']}\n"
desc += f" New MB: {move['MB_curr']}\n\n"
desc += f"Displacement matrix B_{stage}:\n{B_i}\n"
desc += f"Right column: {B_i[:, 2]} = (Δx={move['displacement'][0]}, Δy={move['displacement'][1]}, 1)\n\n"
desc += f"Accumulated product a_{stage}:\n{move['a_t']}\n"
desc += f"Right column: {move['a_t'][:, 2]}\n\n"
desc += f"Delta values: {move['delta']}\n"
desc += f"Kappa vector: {move['kappa']}\n\n"
desc += f"Verification:\n"
desc += f" a_{stage} · c₀ = {move['a_t'] @ self.c0}\n"
desc += f" Should equal: {np.array([move['SB_position'][0], move['SB_position'][1], 1.0])}\n\n"
desc += "Board state:\n"
desc += self.get_board_state()
return desc
if __name__ == "__main__":
print("\n" + "=" * 70)
print("TIC-TAC-TOE - DISPLACEMENT-BASED B MATRICES")
print("=" * 70 + "\n")
game = ABCModelDynamic(n=2, t=1.0, T=1.41)
# Play complete game
moves = [
((-1, -1), 1), # Move 1: X at (-1,-1)
((0, 0), 2), # Move 2: O at (0,0)
((0, -1), 1), # Move 3: X at (0,-1)
((1, 0), 2), # Move 4: O at (1,0)
]
for pos, player in moves:
game.add_move(pos[0], pos[1], player)
# Show first 4 moves in detail
for i in range(1, 5):
print(game.describe_move(i))
print()
# Verification
print("=" * 70)
print("VERIFICATION")
print("=" * 70 + "\n")
print("Key property: MB position tracks piece positions on SB")
for i in range(1, len(game.history) + 1):
move = game.history[i - 1]
MB_curr = move['MB_curr']
SB_pos = move['SB_position']
matches = np.allclose(MB_curr[:2], SB_pos)
print(f"Move {i}: MB={MB_curr} SB={SB_pos} Match? {matches} {'✓' if matches else '❌'}")
print(f"\nB matrices (right columns = displacements):")
for i in range(1, len(game.history) + 1):
move = game.history[i - 1]
B_i = game.B_blocks[i - 1]
disp = move['displacement']
right_col = B_i[:, 2]
print(f" Move {i}: Δ={disp} → B right column = {right_col} ✓")
print(f"\nKappa vectors (tokenized features):")
for i in range(len(game.history)):
kappa = game.kappa[i]
print(f" Piece {i}: κ = {kappa}")
print(f"\nSpecial case - Move 2:")
move2 = game.history[1]
print(f" From (-1, -1) to (0, 0)")
print(f" Displacement: {move2['displacement']}")
print(f" B₂ right column: {game.B_blocks[1][:, 2]} = (1, 1, 1)ᵀ ✓✓✓")
print("\n✓ MB position correctly tracks SB piece positions!")
print("✓ B matrices represent displacements!")
print("✓ B₂ has right column (1, 1, 1)ᵀ as expected!")
print("✓ Kappa vectors initialized correctly!")
print("=" * 70 + "\n") |