Keywords: jupyter notebook; autoreload; heuristics for layout, display, splitting cells, kernel management, \%%magic; setup.py, libraries
| Presenter | James Powell james@dutc.io |
| Date | Friday, January 15, 2021 |
| Time | 3:30 PM EST |
print("Let's go!")
The game “Battlegame” (patent-pending) is played as follows:
Task: model the game above, using what you have learnt.
Task: model same strategies, and build a framework for running these strategies against each other.
from itertools import product
from random import shuffle
def random_strategy(board_size=10):
targets = [*product(range(board_size), range(board_size))]
shuffle(targets)
while True:
yield targets.pop()
def linear_strategy(board_size=10):
targets = [*product(range(board_size), range(board_size))]
targets.reverse()
while True:
yield targets.pop()
The game “Rock, Paper, Scissors” is played as follows:
Task: write a function to evaluate the rules of the game.
# NOTE: for naming & design purposes, you may assume the players are directional
# i.e., `a` is the Player
# `b` is the Challenger
# e.g., `rules` could return "player wins" or "player loses"
# or it could "player wins" vs "challenger wins"
# QUESTION: how do you represent ties?
def rules(a, b):
''' return who wins, given shapes played by two players a and b '''
pass
Task: write a framework that can evaluate a strategy and play the game for 10,000 rounds given a pairing of strategies.
from random import choice
def random_strategy():
''' randomly select a shape '''
return choice(['rps'])
# other sample strategies…
# QUESTION: how do we track "history" here?
def beat_previous_play():
''' select the shape that would beat the opponent's previous play '''
pass
def most_common_play(n=3):
''' select the most common shape from the opponent's previous N plays '''
pass
games = [(random_strategy(), random_strategy()) for _ in range(10_000)]
results = [rules(a, b) for a, b in games]
from enum import Enum, auto
from functools import total_ordering
from collections import namedtuple, Counter
from itertools import product, islice, tee, combinations
from random import shuffle
@total_ordering
class Suits(Enum):
Clubs = 0
Diamonds = auto()
Hearts = auto()
Spades = auto()
def __lt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value
def __hash__(self):
return hash(self.value)
def __str__(self):
return ['\N{black club suit}',
'\N{black diamond suit}',
'\N{black spade suit}',
'\N{black heart suit}',][self.value]
@total_ordering
class Faces(Enum):
Two = 0
Three = auto()
Four = auto()
Five = auto()
Six = auto()
Seven = auto()
Eight = auto()
Nine = auto()
Ten = auto()
Jack = auto()
Queen = auto()
King = auto()
Ace = auto()
def __lt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value
def __hash__(self):
return hash(self.value)
def __str__(self):
return '2 3 4 5 6 7 8 9 10 J Q K A'.split()[self.value]
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
class HandType(namedtuple('HandType', 'predicate')):
__call__ = lambda s, *a, **kw: s.predicate(*a, **kw)
@total_ordering
class Hands(Enum):
RoyalFlush = HandType(
lambda cs: Hands.StraightFlush(cs) and max(cs, key=lambda c: c.face).face == Faces.Ace
)
StraightFlush = HandType(
lambda cs: Hands.Straight(cs) and Hands.Flush(cs)
)
Flush = HandType(
lambda cs: Counter(c.suit for c in cs).most_common(1)[0][-1] == 5
)
FourOfAKind = HandType(
lambda cs: Counter(c.face for c in cs).most_common(1)[0][-1] == 4
)
Straight = HandType(
lambda cs: all(y.face.value - x.face.value == 1
for x, y in nwise(sorted(cs, key=lambda c: c.face)))
)
FullHouse = HandType(
lambda cs: [c for _, c in Counter(c.face for c in cs).most_common(2)] == [3, 2]
)
TwoPair = HandType(
lambda cs: [c for _, c in Counter(c.face for c in cs).most_common(2)] == [2, 2]
)
ThreeOfAKind = HandType(
lambda cs: Counter(c.face for c in cs).most_common(1)[0][-1] == 3
)
TwoOfAKind = HandType(
lambda cs: Counter(c.face for c in cs).most_common(1)[0][-1] == 2
)
HighCard = HandType(
lambda cs: True
)
def __call__(self, *args, **kwargs):
return self.value(*args, **kwargs)
def __lt__(self, other):
return self.value < other.value
def __eq__(self, other):
return self.value == other.value
def __hash__(self):
return hash(self.value)
class Card(namedtuple('Card', 'face suit')):
def __str__(self):
return f'{self.face!s}{self.suit!s}'
class Hand(namedtuple('HandBase', 'cards best_hand')):
@classmethod
def from_cards(cls, *cards):
best_hand = [hand_type for hand_type in Hands if hand_type(cards)]
return cls(cards, best_hand)
deck = [Card(f, s) for f, s in product(Faces, Suits)]
shuffle(deck)
flop = [deck.pop(), deck.pop(), deck.pop()]
hand = [deck.pop(), deck.pop()]
turn = [deck.pop()]
river = [deck.pop()]
h = [*flop, *hand, *turn, *river]
print(f'{Hand.from_cards(*flop, *hand, *turn, *river).best_hand = }')
# from contextlib import contextmanager
# from time import perf_counter
# @contextmanager
# def timed():
# try:
# start = perf_counter()
# yield
# finally:
# stop = perf_counter()
# print(f'Elapsed (\N{greek capital letter delta}t): {stop - start:.2f}s')
# with timed():
# all_possible_hands = Counter(Hand.from_cards(*cs).best_hand
# for cs in islice(combinations(deck, 5), 10_000_000))
# all_possible_hands = Counter()
# for idx, cs in islice(combinations(deck, 5)):
# all_possible_hands[Hand.from_cards(*cs).best_hand] += 1
# print(f'{all_possible_hands = }')
tuple in Pythonclass Loan:
pass
class Bond(Loan):
pass
from tracemalloc import start, take_snapshot
def poke(x):
pass
class Dataset:
def __init__(self, data):
self.data = data
def poke(self):
for x in self.data:
poke(x)
def __getitem__(self):
pass
class Element:
__slots__ = *'xy',
def __init__(self, x, y):
self.x, self.y = x, y
# restricted computation domain
# - numpy.ndarray
# - pandas.DataFrame
start()
before = take_snapshot()
xs = Dataset([Element(0, 0) for _ in range(2_000)])
after = take_snapshot()
xs.poke()
for line in after.compare_to(before, 'lineno')[:1]:
print(line)
# from sys import getsizeof
# print(f'{getsizeof(Element(10, 20)) = }')
# print(f'{Element(10, 20).__dict__ = }')
# print(f'{(10 ** 2000).bit_length() = :,}')
from numpy import array
xs = array([1, 2, 3, 2**65])
print(f'{xs = }')
from contextlib import contextmanager
from time import perf_counter
@contextmanager
def timed(msg):
try:
start = perf_counter()
yield
finally:
end = perf_counter()
print(f'{msg:<20} \N{greek capital letter delta}t: {end-start:.4f}s')
size = 5_000_000
from random import randint
xs = [randint(-1_000, 1_000) for _ in range(size)]
from numpy.random import randint
ys = randint(-1_000, 1_000, size=size)
with timed('list: `sum`'):
sum(xs)
with timed('list: dumb loop'):
total = 0
for x in xs:
total += x
with timed('numpy'):
ys.sum()
with timed('numpy/unboxing'):
total = 0
for x in ys:
total += x
from numpy import array
xs = array([1, 2, 3])
print(f'{xs.__array_interface__["data"][0] = :#_x}')
from numpy import array
from numpy.lib.stride_tricks import as_strided
def setitem(t, i, v):
xs = array([], dtype='uint64')
loc = xs.__array_interface__['data'][0]
idx = id(t) - loc
xs = as_strided(xs, strides=(1,), shape=(idx + 1,))
ys = as_strided(xs[idx:], strides=(8,), shape=(4,))
zs = as_strided(ys[3:], strides=(8,), shape=(i + 1,))
ys[2] += max(0, i - (end := len(t)) + 1)
zs[min(i, end):] = id(v)
t = 0, 1, 2, None, 4, 5
print(f'{t = }')
setitem(t, 3, 3)
print(f'{t = }')
from contextlib import contextmanager
from time import perf_counter
@contextmanager
def timed(msg):
try:
start = perf_counter()
yield
finally:
end = perf_counter()
print(f'{msg:<20} \N{greek capital letter delta}t: {end-start:.4f}s')
from numpy.random import normal
xs = normal(size=(1_000_000, 50))
with timed('.sum(axis=0)'):
xs.sum(axis=0)
xs = normal(size=(50, 1_000_000))
with timed('.sum(axis=1)'):
xs.sum(axis=1)
# with timed('.T.sum(axis=0)'):
# with timed(' .T.copy()'):
# ys = xs.T.copy()
# with timed(' ys.sum(axis=0)'):
# ys.sum(axis=0)
# print(f'{xs.strides = }')
# print(f'{ys.strides = }')
from pandas import Interval, array
xs = array([
Interval(0, 4),
Interval(4, 8),
])
print(f'{dir(xs) = }')
collections.namedtuple & dataclasses.dataclassfrom collections import namedtuple
from dataclasses import dataclass
from enum import Enum, auto
# eval/exec
class T(namedtuple('T', 'x y')):
pass
print(f'{T(10, 20) = }')
print(f'{T(10, 20).x = }')
# class decorator
class Base:
pass
@dataclass
class T(Base):
x : int
y : int
z : int = 100
print(f'{T(10, 20) = }')
print(f'{help(T) = }')
# metaclass/__init_subclass__
class T(Enum):
X = auto()
Y = auto()
class Base:
def __init_subclass__(cls):
print(cls)
class Derived(Base):
pass
print(f'{T.X = }')
print(f'{T.Y = }')
print(f'{T["X"] is T.X = }')
print(f'{[*T] = }')
def __build_class__(*args, bc=__build_class__, **kwargs):
print(f'__build_class__(*{args}, **{kwargs})')
return bc(*args, **kwargs)
import builtins
builtins.__build_class__ = __build_class__
class T:
pass
class X:
pass
import json
import pandas
from collections import Counter
c1 = Counter('aabccccddddd')
c2 = Counter('aaaaabbcccdd')
print(f'{c1 = }')
print(f'{c2 = }')
print(f'{c1 + c2 = }')
print(f'{c1["a"] = }')