File: android-tkinter/CODE/tictactoe.py (original) (raw)
"""
[SA] Sep-2017: Standalone release of PyCalc, PyClock, PyPhoto, PyToe. Copyright 2017 M.Lutz, from book "Programming Python, 4th Edition". License: provided freely, but with no warranties of any kind. This is main logic; see tictactoe_lists.py for the meat of the system. 2.0: added DemoMode config: if True, displays N preset boards/games.
ANDROID version, Jan 2019 (see "# ANDROID" for changes)
======================================================================= """
[PP4E] this file has been updated for Python 3.X
import sys, copy # ANDROID: tkinter import must be on a line by itself, import tkinter # ANDROID: else the GUI support is not loaded/present
from tictactoe_lists import * # move-mode subclasses from tictactoe_lists import helpdisplay # common help utility
from getConfigs import getConfigs # file-or-args configs from getConfigs import attrsToDict, dictToAttrs # for passing **kargs
[SA] Mac and Linux port things
RunningOnMac = sys.platform.startswith('darwin') RunningOnLinux = sys.platform.startswith('linux') RunningOnWindows = sys.platform.startswith('win')
[SA]: set window icons on Windows and Linux
from windowicons import trySetWindowIcon
#----------------------------------------------------------------------
Game object generator - external interface
#----------------------------------------------------------------------
def TicTacToe(root, Mode, **args): # this consumes Mode classname = 'TicTacToe' + Mode # e.g., -mode Minimax try: classobj = eval(classname) # get class by string name except: print('Bad Mode option value:', mode) raise # reraise else: # [SA] was eval(classname)(**args) return classobj(root, **args) # run class constructor (3.x: was apply())
#----------------------------------------------------------------------
Configurations interface - from file or cmdline args, with defaults
#----------------------------------------------------------------------
defaultConfigs = dict( DemoMode=False, # use preset boards? InitialSize=None, # WxH, '200x300' BgColor='wheat', FgColor='black', Font=('courier', 50, 'bold'), # use 'family...' if arg Degree=3, # 3 across = tic-tac-toe GoesFirst='user', # 'user' or 'machine' UserMark='X', # 'X' or 'O' Mode='Minimax') # 5 classes in module
def userconfigs(): """ [SA] new common gadgets utility: file or cmdline e.g., python3 tictactoe.py -configs ~/myconfigs.py e.g., python3 tictactoe.py -Degree 4 -Mode Expert2 -Font 'menlo 40' """ configs = getConfigs('PyToe', defaultConfigs) # load from file or args configs.Degree = int(configs.Degree) # str->int iff needed return configs
#----------------------------------------------------------------------
Board builders - 1/configs for normal mode, N/presets for demo mode
#----------------------------------------------------------------------
def makeBoard(configs, kind=Tk): """ create 1 board, according to configs """ root = kind() trySetWindowIcon(root, 'icons', 'pygadgets') # [SA] for win+lin if configs.InitialSize: # None size is probably best root.geometry(configs.InitialSize) frm = TicTacToe(root, **attrsToDict(configs)) # build board frame on root if kind == Tk: setAppleReopen(root) # works on Tk only, DemoMode is just one process
# [SA] question=? but portable, help key in all gadgets
root.bind('<KeyPress-question>', lambda event: helpdisplay(root))
def demoMode(configs): """ create N boards per preset configs for variety and demo """ class DemoWindow(GuiMakerWindowMenu): """ a Frame on a simple Tk root: global help and quit, quit closes all windows; boards are Toplevels: board=>quit closes just itself; """ appname = 'PyToe' # for new guimaker
def start(self):
self.helpButton = False
# [SA] Mac OS help automatic in guimaker
if not RunningOnMac:
self.menubar = [('Help', 0, [('About', 0, self.onAbout, '*-h')] )]
def makeWidgets(self):
greeting = Label(self, text='Welcome to PyToe DemoMode', padx=5, pady=3)
greeting.config(font=('times', 20, 'italic'))
greeting.pack(expand=YES, fill=BOTH)
greeting.bind('<Button-1>', lambda event: helpdisplay(root)) # for fun
def onAbout(self):
helpdisplay(self)
onHelp = onAbout # [SA] for Mac OS, new guimaker
root = Tk()
root.title('PyToe 2.0')
trySetWindowIcon(root, 'icons', 'pygadgets') # for Win+Lin
setAppleReopen(root) # for Mac
frm = DemoWindow(root)
# [SA] question=? but portable, help key in all gadgets
root.bind('<KeyPress-question>', lambda event: helpdisplay(root))
# attrs may have been set in file or cmd
teal = 'teal' if tkinter.TkVersion >= 8.6 else '#006e6d' # in 8.6+
modconfigs = [
dict(Degree=5, Mode='Expert2', BgColor=teal, FgColor='cyan'),
dict(Degree=4, Mode='Expert2', BgColor='wheat', FgColor='black'),
dict(Degree=3, Mode='Minimax', BgColor='#173166', FgColor='white'),
dict(Degree=2, Mode='Minimax', BgColor='#633025', FgColor='white')]
# popup smallest last = on top: last made is drawn first
# Linux scatters the boards, but best on all platforms
if RunningOnMac or RunningOnWindows or RunningOnLinux:
modconfigs = reversed(modconfigs)
# overide settings or defaults
for modconfig in modconfigs:
democonfigs = attrsToDict(configs)
democonfigs.update(modconfig)
democonfigs = dictToAttrs(democonfigs)
democonfigs.InitialSize = None
makeBoard(democonfigs, kind=Toplevel)
# else covered and not focused on Windows and Linux
if RunningOnWindows or RunningOnLinux:
root.focus() # this works and suffices on both
#frm.lift() # root.lift leaves hidden on Windows
#frm.focus_force() # so does root.after_idle(root.lift)
def setAppleReopen(root): """ configure standard events """ if RunningOnMac: # Mac requires menus, deiconifies, focus # [SA] reopen auto on dock/app click and fix tk focus loss on deiconify def onReopen(): root.lift() root.update() temp = Toplevel() temp.lower() temp.destroy() root.createcommand('::tk::mac::ReopenApplication', onReopen)
#----------------------------------------------------------------------
Main logic - fetch options from file or command-line, run game
#----------------------------------------------------------------------
if name == 'main': configs = userconfigs() if configs.DemoMode: demoMode(configs) else: makeBoard(configs) mainloop() # and wait for user to play...