File: pygadgets-products/unzipped/_PyToe/TicTacToe/unconverted-python2X/temp.notes.txt
***PLEASE IGNORE THIS FILE***
_grid...
# Intro: Of course, you can get a lot of work done without
# Canvases too. In fact, the ideas of packing frames and
# using grids are often suffiecient for contructing complex
# GUI layouts. Our next example demonstrate this idea: it
# uses a simple grid of labels to build a game board.
# Tic-tac-toe, naughts-and-crosses, N-across
# by default, 3-across=ttt, but can vary Degree to change
# board size (the number of row and column)
#
# - this is an application of grids, but too complex
# to show where grids introduced--new case study at end
# of gui chapter (?)
# - probs: pack mixed with grid if GuiMaker is used
# - squares = {} # or append cell to row, row to cols
# - cells and board share same State objects--changes to
# one auto noticed by other ref--ex of where shared objs
# are a Good Thing (could use lists instead of State attrs);
#
# add GuiMaker menus - quit, help=about box
# from guimaker import GuiMakerWindow
#
# alt in CountAcrossDown:
# countRows = []
# for row in range(self.degree):
# countRows.append(tally.copy())
# for col in range(self.degree):
# mark = board[(row, col)]
# countRows[row][mark] = countRows[row][mark] + 1
#
# ??? Degree chould probably be a self.attr so can vary per
# instance in process (as is, can change module-level var
# from outside, but effects all games created before or after)
# -> fixed, but mention diff in text
#
# mention uses dicts for board/counts; alt = [[0]*Degree]*Degree
# simulates a 2D matrix with a 1D hashtable, where keys=row,col
# tuples; (hash + tuple-compare) versus (offset + offset) -> faster?
#
# - mention minimax move lookahead search, versus heuristics only (see sbar)
# - mention tuple unpacking assigment in heuristic header
# - mention: originally had all moves selection logic in one
# big/long method; much more readable when split out into
# separate methods--rule of thumb: keep methods short!
# if you find yourself adding comments before a big block of
# code because you can't understand what it does, it's probably
# time to move that block into a named method of its own.
# - mention that could use frames and pack here too--1 per row,
# but since I"m goin gto show you how to do this to build a
# calculator in the text processing chapter, I'll use grid here;
#
# ??? test degree = 4, 5, 2; dumb mode
# ??? expand or change heuristic score logic?
# ??? GuiMaker not set up for grid() completely--makeWidgets, framemenus
# ??? resizing doesnt work
# ??? smart mode not working
# --- self.quit fails; Frame.quit(self) works; why???
# NO->Frame.quit fails too; need sys.exit()
# depends on what is next after test end call;
# exits if nothing, else returns (quit doesn't end process)
#
#####################################################################
# Sidebar: Is PyToe artificially intelligent? (end of chapter)
# Well, maybe, but it depends on what you call intelligent.
# PyToe is smart enough to notice imminent wins and losses,
# and to reasonably select from a set of alternative moves.
# When a win or loss isn't imminent, it uses a scoring scheme,
# sometimes called a _heuristic_, to rank the "goodness" of
# alternative moves.
#
# But PyToe's view is limited to the next move; it's unable to
# look ahead through a series of moves and countermoves to
# analyse the consequences of a given move. Especially for
# more complex two-player games such as chess, it's more
# traditional to search through all possible moves and
# countermoves, and pick moves that maximize the chances of
# reaching a win, and minimize moves that can lead to a loss.
# In fact, there's a name for this technique: _minimax_ search.
# (VERIFY NAME)
#
# Minimax game search is described fully in most AI texts. But
# roughly, such a search generates new boards ("states") for each
# possible move, with the candidate move made. For each new board,
# it then generates boards with all possible countermoves, and so
# on, generating all possible moves at each level, and detecting
# wins and loses along the way. Such a search lets the program
# look ahead in the game to be more informed about the consequences
# of its actions. In practice, the search may stop after so many
# levels of lookahead, and apply a heuristic to score the resulting
# board state; especially for complex games or large boards, it's
# not practical to explore all possible moves and countermoves
# to end game states (you'd run out of time and memory). PyToe
# picks moves with some intelligence, but has no notion of lookahead;
# that may be okay for this simple game, but may hurt in others.
#
# But can PyToe be called an "expert system"--a system that encodes
# and applies expert-level knowledge? Again, maybe. In some sense,
# its move selection logic can be viewed as domain knowledge, encoded
# in the Python language. In fact, selects moves more or less the
# way I would (though I usually look ahead to possible countermoves
# too). General expert systems, though, usually encode domain
# knowledge rules in a more symbolic form, and apply it using
# more general-purpose algorithms called "inference engines".
# We'll see such a system in the last chapter in this part of
# the book--the PyExpert expert system shell.
#
# All of which doesn't quite answer the question this sidebar
# raises. If pressed, I'd say you could call PyToe intelligent
# if you can't tell it's a machine, and if it's smart enough
# to beat you on a regular basis. For me, that's a positive.
####################################################################
_dict...
# poss opts:
# x make board just marks (not State), use board.copy() (not deepcopy) [this]
# - don't board.copy(): make move, recur, unmake move (ok, since depth-1st)
# - use arrays for board, indexing (not hash + compare)
# - use scoreMove at cutoff depth
#
# see tictactoe_times.txt
#
# Poss: entire part3 chapter on AI:
# - functional programming tools
# - gaming
# - holmes
# Intro: Of course, you can get a lot of work done without
# Canvases too. In fact, the ideas of packing frames and
# using grids are often sufficient for contructing complex
# GUI layouts. Our next example demonstrate this idea: it
# uses a simple grid of labels to build a game board.
# Tic-tac-toe, naughts-and-crosses, N-across
# by default, 3-across=ttt, but can vary Degree to change
# board size (the number of row and column)
#
# - this is an application of grids, but too complex
# to show where grids introduced--new case study at end
# of gui chapter (?)
# - probs: pack mixed with grid if GuiMaker is used
# - squares = {} # or append cell to row, row to cols
# - cells and board share same State objects--changes to
# one auto noticed by other ref--ex of where shared objs
# are a Good Thing (could use lists instead of State attrs);
#
# add GuiMaker menus - quit, help=about box
# from Gui.Tools.guimaker import GuiMakerWindow
#
# alt in CountAcrossDown:
# countRows = []
# for row in range(self.degree):
# countRows.append(tally.copy())
# for col in range(self.degree):
# mark = board[(row, col)]
# countRows[row][mark] = countRows[row][mark] + 1
#
# --- Degree csould probably be a self.attr so can vary per
# instance in process (as is, can change module-level var
# from outside, but effects all games created before or after)
# -> fixed, but mention diff in text
#
# mention uses dicts for oard/counts; alt = [[0]*Degree]*Degree
# simulates a 2D matrix with a 1D hashtable, where keys=row,col
# tuples; (hash + tuple-compare) versus (offset + offset) -> faster?
#
# - mention minimax move lookahead search, versus heuristics only (see sbar)
# - mention tuple unpacking assigment in heuristic header
# - mention: originally had all moves selection logic in one
# big/long method; much more readable when split out into
# separate methods--rule of thumb: keep methods short!
# if you find yourself adding comments before a big block of
# code because you can't understand what it does, it's probably
# time to move that block into a named method of its own.
# - mention that could use frames and pack here too--1 per row,
# but since I"m goin gto show you how to do this to build a
# calculator in the text processing chapter, I'll use grid here;
#
# grid resolution--use pack instead; to much of a hack to call
# makeWidgets outside of guimaker classes (need to avoid toplevel
# frame completely, else won't resize correctly--need grids within
# grids, not frames)
#
# --- test degree = 4, 5, 2; naive/dumb mode
# --- expand or change heuristic score logic?
# ??? GuiMaker not set up for grid() completely--makeWidgets, framemenus
# --- resizing doesnt work (does for frames)
# --- smart mode not working
# --- self.quit fails; Frame.quit(self) works; why?
# NO->Frame.quit fails too; need sys.exit()
# depends on what is next after test end call;
# exits if nothing, else returns (quit doesn't end process)
# ---show cmdline exs:
# TicTacToe.py -degree 5 -mode naive -bg blue -fg white -fontsz 30
# TicTacToe.py -degree 10 -fontsz 30
# ---mention precount so each test is a single hash op, not array scan
#
# ---isWinFor could reuse checkWin logic; move, ckeck, undo
# changed -> now does
#
# coding alt:
# Notice how the smart mode's move selection logic detects
# imminent wins and losses: it mark a candidate move on
# the board, rune the end-game win detector, and then unmarks
# the candidate move. It plays a sort of 'what if' experiment.
# I originally coded smart mode's isWin/isLoss tests
# to use the count arrays rather than the end-game win check,
# because I thought it would be faster (hashing versus multiple
# table scans). As it turned out, pickMove was fast enough to
# appear instantaneous, so I used the end-game chack instead.
# In fact, move selection is so fast, that I decide to add a
# 2-secnd delay (time.sleep(2)), to make it easier for the user
# to notice which move hte machine picked. (Years of using
# slower AI languages have made me paranoid).
#
# def isWinFor(self, playerMark,
# (row, col),
# ((countRows, countCols), (countDiag1, countDiag2)) ):
# return (
# (countCols.get((col, playerMark), 0) == self.degree-1)
# or
# (countRows.get((row, playerMark), 0) == self.degree-1)
# or
# (row == col and
# countDiag1[playerMark] == self.degree-1)
# or
# (row + col == self.degree-1 and
# countDiag2[playerMark] == self.degree-1) )
#
# def isWin(self, move, countMarks):
# return self.isWinFor(self.machineMark, move, countMarks)
# def isBlock(self, move, countMarks):
# return self.isWinFor(self.userMark, move, countMarks)
#
#--nasty mode seems to lose for -degree 10 -- misses block!
# FIXED: wasusing Degree, not self.degree, and need to divide
# simplr score by board size, else gets larger than
# moves where patterns kicked in
#--center not used in nasty
# OK! -> the diagonals don't intersect in an even-degree
# board (e.g., 10x10), but do if odd (9x9)
#--remove trace calls
#
#???--can be beaten if pick corners
#???--write grid/loop versionsas subclasses
#
#--alt: empties = filter((lambda coord, b=board: b[coord].mark == Empty),
# board.keys())
# ???apply static score at cutoff levels
# ???verify minimax working: maxdepth correct? values propogating ok?
#
# ??? minimax can still be beaten, if top row picked (lists) or
# left col picked (dict)
#
#####################################################################
####################################################################
_lists...
# poss opts:
# x make board just marks (not State), use board.copy() (not deepcopy)
# x use arrays for board, indexing (not hash + compare) [this]
# also speeds draw and checkWin - use 'in' and list.count on entire row
# - don't board.copy-- just set mark, recure, set Empty [has little effect]
# - use scoreMove at cutoff depth
# - checkFinish should only look for wins at move just made, not all r,c
#
# Record class replaces record dictionary
#
# see tictactoe_times.txt:
#
# 4 times slower on DC power (better diffs, == slower machine)
# (occasionally almost as fast on DC, iff CPU not scaled back when starts)
#
# 2D lists with row,col,player in checkWin???
# 2D lists are faster than dict board (1 sec at slow CPU)
# mark/reur/empty not much faster than board.copy()
# board.copy() much faster than copy.deepcopy()
#
# with lists, sometimes delays win, if can make a move that leads to
# a win 2 ways; depends on order of empties on board
#
# note that order of move picks varies between list and dict alts,
# because list alt checks empties by row and col, and dict alt
# checks empties by order in dict--board.keys(), which is random,
# diff from fixed row/col scan; dict and list versions pick moves
# identically if dict version uses board.sort() to get fixed order:
# in findMin and findMax:
#
# b = board.keys()
# b.sort() # order search by (row, col) tuples
# for move in b:
# #for move in board.keys():
#
# enable commented-out traces to see boards
#
# note that could also implement board as Button widgets, with 'command'
# callbacks (se calculator gui in lang chapter); here, coded as labels
# with mouse buttin press bind callbacks; effect is similar (but no
# depress effect on push like you get with buttons);
#
# Poss: entire part3 chapter on AI:
# - functional programming tools
# - gaming
# - holmes
# Intro: Of course, you can get a lot of work done without
# Canvases too. In fact, the ideas of packing frames and
# using grids are often sufficient for contructing complex
# GUI layouts. Our next example demonstrate this idea: it
# uses a simple grid of labels to build a game board.
# Tic-tac-toe, naughts-and-crosses, N-across
# by default, 3-across=ttt, but can vary Degree to change
# board size (the number of row and column)
#
# - this is an application of grids, but too complex
# to show where grids introduced--new case study at end
# of gui chapter (?)
# - probs: pack mixed with grid if GuiMaker is used
# - squares = {} # or append cell to row, row to cols
# - cells and board share same State objects--changes to
# one auto noticed by other ref--ex of where shared objs
# are a Good Thing (could use lists instead of State attrs);
#
# add GuiMaker menus - quit, help=about box
# from guimaker import GuiMakerWindow
#
# alt in CountAcrossDown:
# countRows = []
# for row in range(self.degree):
# countRows.append(tally.copy())
# for col in range(self.degree):
# mark = board[(row, col)]
# countRows[row][mark] = countRows[row][mark] + 1
#
# --- Degree csould probably be a self.attr so can vary per
# instance in process (as is, can change module-level var
# from outside, but effects all games created before or after)
# -> fixed, but mention diff in text
#
# mention uses dicts for oard/counts; alt = [[0]*Degree]*Degree
# simulates a 2D matrix with a 1D hashtable, where keys=row,col
# tuples; (hash + tuple-compare) versus (offset + offset) -> faster?
#
# - mention minimax move lookahead search, versus heuristics only (see sbar)
# - mention tuple unpacking assigment in heuristic header
# - mention: originally had all moves selection logic in one
# big/long method; much more readable when split out into
# separate methods--rule of thumb: keep methods short!
# if you find yourself adding comments before a big block of
# code because you can't understand what it does, it's probably
# time to move that block into a named method of its own.
# - mention that could use frames and pack here too--1 per row,
# but since I"m goin gto show you how to do this to build a
# calculator in the text processing chapter, I'll use grid here;
#
# grid resolution--use pack instead; to much of a hack to call
# makeWidgets outside of guimaker classes (need to avoid toplevel
# frame completely, else won't resize correctly--need grids within
# grids, not frames)
#
# --- test degree = 4, 5, 2; naive/dumb mode
# --- expand or change heuristic score logic?
# ??? GuiMaker not set up for grid() completely--makeWidgets, framemenus
# --- resizing doesnt work (does for frames)
# --- smart mode not working
# --- self.quit fails; Frame.quit(self) works; why?
# NO->Frame.quit fails too; need sys.exit()
# depends on what is next after test end call;
# exits if nothing, else returns (quit doesn't end process)
# ---show cmdline exs:
# TicTacToe.py -degree 5 -mode naive -bg blue -fg white -fontsz 30
# TicTacToe.py -degree 10 -fontsz 30
# ---mention precount so each test is a single hash op, not array scan
#
# ---isWinFor could reuse checkWin logic; move, ckeck, undo
# changed -> now does
#
# coding alt:
# Notice how the smart mode's move selection logic detects
# imminent wins and losses: it mark a candidate move on
# the board, rune the end-game win detector, and then unmarks
# the candidate move. It plays a sort of 'what if' experiment.
# I originally coded smart mode's isWin/isLoss tests
# to use the count arrays rather than the end-game win check,
# because I thought it would be faster (hashing versus multiple
# table scans). As it turned out, pickMove was fast enough to
# appear instantaneous, so I used the end-game chack instead.
# In fact, move selection is so fast, that I decide to add a
# 2-secnd delay (time.sleep(2)), to make it easier for the user
# to notice which move hte machine picked. (Years of using
# slower AI languages have made me paranoid).
#
# def isWinFor(self, playerMark,
# (row, col),
# ((countRows, countCols), (countDiag1, countDiag2)) ):
# return (
# (countCols.get((col, playerMark), 0) == self.degree-1)
# or
# (countRows.get((row, playerMark), 0) == self.degree-1)
# or
# (row == col and
# countDiag1[playerMark] == self.degree-1)
# or
# (row + col == self.degree-1 and
# countDiag2[playerMark] == self.degree-1) )
#
# def isWin(self, move, countMarks):
# return self.isWinFor(self.machineMark, move, countMarks)
# def isBlock(self, move, countMarks):
# return self.isWinFor(self.userMark, move, countMarks)
#
#--nasty mode seems to lose for -degree 10 -- misses block!
# FIXED: wasusing Degree, not self.degree, and need to divide
# simplr score by board size, else gets larger than
# moves where patterns kicked in
#--center not used in nasty
# OK! -> the diagonals don't intersect in an even-degree
# board (e.g., 10x10), but do if odd (9x9)
#--remove trace calls
#
#???--can be beaten if pick corners
# - too pesimistic; if all look like min can win (-1),
# use static eval at top;
# => DONE, 11/11 (can still beat, but not by
# picking all in top row, r-to-l)
#???--write grid/loop version as subclasses
#???--add text mode as subclass; loop version?
#???--recode to only check win for last mover (or exercise)
#
#--alt: empties = filter((lambda coord, b=board: b[coord].mark == Empty),
# board.keys())
#--this fails: self.board = [[0] * self.degree] * self.degree
#--note 3L formula in expert2; else overflows normal int
#
# ???apply static score at cutoff levels
# ???verify minimax working: maxdepth correct? values propogating ok?
#
#####################################################################
#
# exponential nature of game tree searches
# minimax not practical for larger boards
# as is, at degree=5, after opening move by user, there are:
# 24 * 23 * 22 * 21 * 20 = 5,100,480 board states to analyse
# - 25 slots - 1 taken = 24 possible moves
# - for each othe 24 possible moves, there are 23 countermoves (2 taken)
# - for each of the 23 countermoves, 22 possible next moves (3 taken)
# - maxDepth will be 1 (numer marks) + 4 = 5 levels deep
# work totally pointless on larger boards, since neither player will
# reach a win after just 5 moves
# need to stop and statically score board a some fixed depth
# alpha-beta cutoff helps trim search space
#
# self.after_idle(self.machineMove)
# doe not work to keep gui active while piccking move;
# no widget updates while 'thinking'; could code move
# picker as a thread, call self.update before start a
# new search node, etc.
#
#show 3 alts: all poss dueto fact that classes are rt objs, not decls
#
#def TicTacToe(mode=Mode, **args):
# try:
# classname = 'TicTacToe' + mode # e.g., -mode Minimax
# classobj = eval(classname) # get class by string name
# except:
# print 'Bad -mode flag value:', mode
# return apply(eval(classname), (), args) # run class constructor
#
#
# classes = {'random': TicTacToeRandom,
# 'smart': TicTacToeSmart,
# 'expert1': TicTacToeExpert1,
# 'expert2': TicTacToeExpert2,
# 'minimax': TicTacToeMinimax}
# try:
# return apply(classes[mode], (), args)
# except KeyError:
# print 'Bad -mode flag value:', mode
#
#
# if mode == 'random':
# return apply(TicTacToeRandom, (), args)
# if mode == 'smart':
# return apply(TicTacToeSmart, (), args)
# if mode == 'expert1':
# return apply(TicTacToeExpert1, (), args)
# if mode == 'expert2':
# return apply(TicTacToeExpert2, (), args)
# if mode == 'minimax':
# return apply(TicTacToeMinimax, (), args)
# assert 0, 'Bad mode argument'
#####################################################################
# Sidebar: Is PyToe artificially intelligent? (end of chapter)
# Well, maybe, but it depends on what you call intelligent.
# PyToe is smart enough to notice imminent wins and losses,
# and to reasonably select from a set of alternative moves.
# When a win or loss isn't imminent, it uses a scoring scheme,
# sometimes called a _heuristic_, to rank the "goodness" of
# alternative moves.
#
# But PyToe's view is limited to the next move; it's unable to
# look ahead through a series of moves and countermoves to
# analyse the consequences of a given move. Especially for
# more complex two-player games such as chess, it's more
# traditional to search through all possible moves and
# countermoves, and pick moves that maximize the chances of
# reaching a win, and minimize moves that can lead to a loss.
# In fact, there's a name for this technique: _minimax_ search.
# (VERIFY NAME)
#
# Minimax game search is described fully in most AI texts. But
# roughly, such a search generates new boards ("states") for each
# possible move, with the candidate move made. For each new board,
# it then generates boards with all possible countermoves, and so
# on, generating all possible moves at each level, and detecting
# wins and loses along the way. Such a search lets the program
# look ahead in the game to be more informed about the consequences
# of its actions. In practice, the search may stop after so many
# levels of lookahead, and apply a heuristic to score the resulting
# board state; especially for complex games or large boards, it's
# not practical to explore all possible moves and countermoves
# to end game states (you'd run out of time and memory). PyToe
# picks moves with some intelligence, but has no notion of lookahead;
# that may be okay for this simple game, but may hurt in others.
# (Acutally, the 'nasty' mode's scoring system accounts for wins
# and losses 2 moves away; but to look further ahead, we'd need
# to kepp adding hard-coded rules, and this is less than a general
# algorithm solution).
#
# But can PyToe be called an "expert system"--a system that encodes
# and applies expert-level knowledge? Again, maybe. In some sense,
# its move selection logic can be viewed as domain knowledge, encoded
# in the Python language. In fact, selects moves more or less the
# way I would (though I usually look ahead to possible countermoves
# too). General expert systems, though, usually encode domain
# knowledge rules in a more symbolic form, and apply it using
# more general-purpose algorithms called "inference engines".
# We'll see such a system in the last chapter in this part of
# the book--the PyExpert expert system shell.
####################################################################
_loop...
# Intro: Of course, you can get a lot of work done without
# Canvases too. In fact, the ideas of packing frames and
# using grids are often suffiecient for contructing complex
# GUI layouts. Our next example demonstrate this idea: it
# uses a simple grid of labels to build a game board.
# Tic-tac-toe, naughts-and-crosses, N-across
# by default, 3-across=ttt, but can vary Degree to change
# board size (the number of row and column)
#
# - this is an application of grids, but too complex
# to show where grids introduced--new case study at end
# of gui chapter (?)
# - probs: pack mixed with grid if GuiMaker is used
# - squares = {} # or append cell to row, row to cols
# - cells and board share same State objects--changes to
# one auto noticed by other ref--ex of where shared objs
# are a Good Thing (could use lists instead of State attrs);
#
# add GuiMaker menus - quit, help=about box
# from guimaker import GuiMakerWindow
#
# alt in CountAcrossDown:
# countRows = []
# for row in range(self.degree):
# countRows.append(tally.copy())
# for col in range(self.degree):
# mark = board[(row, col)]
# countRows[row][mark] = countRows[row][mark] + 1
#
# ??? Degree chould probably be a self.attr so can vary per
# instance in process (as is, can change module-level var
# from outside, but effects all games created before or after)
# -> fixed, but mention diff in text
#
# mention uses dicts for oard/counts; alt = [[0]*Degree]*Degree
# simulates a 2D matrix with a 1D hashtable, where keys=row,col
# tuples; (hash + tuple-compare) versus (offset + offset) -> faster?
#
# - mention minimax move lookahead search, versus heuristics only (see sbar)
# - mention tuple unpacking assigment in heuristic header
# - mention: originally had all moves selection logic in one
# big/long method; much more readable when split out into
# separate methods--rule of thumb: keep methods short!
# if you find yourself adding comments before a big block of
# code because you can't understand what it does, it's probably
# time to move that block into a named method of its own.
# - mention that could use frames and pack here too--1 per row,
# but since I"m goin gto show you how to do this to build a
# calculator in the text processing chapter, I'll use grid here;
#
# ??? test degree = 4, 5, 2; dumb mode
# ??? expand or change heuristic score logic?
# ??? GuiMaker not set up for grid() completely--makeWidgets, framemenus
# ??? resizing doesnt work
# ??? smart mode not working
# --- self.quit fails; Frame.quit(self) works; why???
# NO->Frame.quit fails too; need sys.exit()
# depends on what is next after test end call;
# exits if nothing, else returns (quit doesn't end process)
#
# ??? recoded with explicit play() loop, not sys.exit();
# explain wait_variable(var): puases caller until the
# variable (a Tkinter variable, not plain Python var) has
# changed value; set in button-press handler; other GUI
# actions and events are processed during wait, so screen
# will redraw, File/Quit ends program/gui, etc.; Tkinter
# vars are global in scope, but Tkinter auto generates
# and uses unique Tk var names behind the scenes--treat
# like normal Python objects;
# show older version as alt coding:
# ??? (is the alt more event driven?)
# What if they click mouse before loop gets back to wait?
#
#####################################################################
###################################################################
_slow...
# Poss: entire part3 chapter on AI:
# - functional programming tools
# - gaming
# - holmes
# Intro: Of course, you can get a lot of work done without
# Canvases too. In fact, the ideas of packing frames and
# using grids are often sufficient for contructing complex
# GUI layouts. Our next example demonstrate this idea: it
# uses a simple grid of labels to build a game board.
# Tic-tac-toe, naughts-and-crosses, N-across
# by default, 3-across=ttt, but can vary Degree to change
# board size (the number of row and column)
#
# - this is an application of grids, but too complex
# to show where grids introduced--new case study at end
# of gui chapter (?)
# - probs: pack mixed with grid if GuiMaker is used
# - squares = {} # or append cell to row, row to cols
# - cells and board share same State objects--changes to
# one auto noticed by other ref--ex of where shared objs
# are a Good Thing (could use lists instead of State attrs);
#
# add GuiMaker menus - quit, help=about box
# from guimaker import GuiMakerWindow
#
# alt in CountAcrossDown:
# countRows = []
# for row in range(self.degree):
# countRows.append(tally.copy())
# for col in range(self.degree):
# mark = board[(row, col)]
# countRows[row][mark] = countRows[row][mark] + 1
#
# --- Degree csould probably be a self.attr so can vary per
# instance in process (as is, can change module-level var
# from outside, but effects all games created before or after)
# -> fixed, but mention diff in text
#
# mention uses dicts for oard/counts; alt = [[0]*Degree]*Degree
# simulates a 2D matrix with a 1D hashtable, where keys=row,col
# tuples; (hash + tuple-compare) versus (offset + offset) -> faster?
#
# - mention minimax move lookahead search, versus heuristics only (see sbar)
# - mention tuple unpacking assigment in heuristic header
# - mention: originally had all moves selection logic in one
# big/long method; much more readable when split out into
# separate methods--rule of thumb: keep methods short!
# if you find yourself adding comments before a big block of
# code because you can't understand what it does, it's probably
# time to move that block into a named method of its own.
# - mention that could use frames and pack here too--1 per row,
# but since I"m goin gto show you how to do this to build a
# calculator in the text processing chapter, I'll use grid here;
#
# grid resolution--use pack instead; to much of a hack to call
# makeWidgets outside of guimaker classes (need to avoid toplevel
# frame completely, else won't resize correctly--need grids within
# grids, not frames)
#
# --- test degree = 4, 5, 2; naive/dumb mode
# --- expand or change heuristic score logic?
# ??? GuiMaker not set up for grid() completely--makeWidgets, framemenus
# --- resizing doesnt work (does for frames)
# --- smart mode not working
# --- self.quit fails; Frame.quit(self) works; why?
# NO->Frame.quit fails too; need sys.exit()
# depends on what is next after test end call;
# exits if nothing, else returns (quit doesn't end process)
# ---show cmdline exs:
# TicTacToe.py -degree 5 -mode naive -bg blue -fg white -fontsz 30
# TicTacToe.py -degree 10 -fontsz 30
# ---mention precount so each test is a single hash op, not array scan
#
# ---isWinFor could reuse checkWin logic; move, ckeck, undo
# changed -> now does
#
# coding alt:
# Notice how the smart mode's move selection logic detects
# imminent wins and losses: it mark a candidate move on
# the board, rune the end-game win detector, and then unmarks
# the candidate move. It plays a sort of 'what if' experiment.
# I originally coded smart mode's isWin/isLoss tests
# to use the count arrays rather than the end-game win check,
# because I thought it would be faster (hashing versus multiple
# table scans). As it turned out, pickMove was fast enough to
# appear instantaneous, so I used the end-game chack instead.
# In fact, move selection is so fast, that I decide to add a
# 2-secnd delay (time.sleep(2)), to make it easier for the user
# to notice which move hte machine picked. (Years of using
# slower AI languages have made me paranoid).
#
# def isWinFor(self, playerMark,
# (row, col),
# ((countRows, countCols), (countDiag1, countDiag2)) ):
# return (
# (countCols.get((col, playerMark), 0) == self.degree-1)
# or
# (countRows.get((row, playerMark), 0) == self.degree-1)
# or
# (row == col and
# countDiag1[playerMark] == self.degree-1)
# or
# (row + col == self.degree-1 and
# countDiag2[playerMark] == self.degree-1) )
#
# def isWin(self, move, countMarks):
# return self.isWinFor(self.machineMark, move, countMarks)
# def isBlock(self, move, countMarks):
# return self.isWinFor(self.userMark, move, countMarks)
#
#--nasty mode seems to lose for -degree 10 -- misses block!
# FIXED: wasusing Degree, not self.degree, and need to divide
# simplr score by board size, else gets larger than
# moves where patterns kicked in
#--center not used in nasty
# OK! -> the diagonals don't intersect in an even-degree
# board (e.g., 10x10), but do if odd (9x9)
#--remove trace calls
#
#???--can be beaten if pick corners
#???--write grid/loop versionsas subclasses
#
# empties = filter((lambda coord, b=board: b[coord].mark == Empty),
# board.keys())
#
#####################################################################
#####################################################################