2020-06-05 16:33:30 -07:00
|
|
|
from manimlib.imports import *
|
|
|
|
|
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
def boolian_linear_combo(bools):
|
|
|
|
return reduce(op.xor, [b * n for n, b in enumerate(bools)], 0)
|
|
|
|
|
|
|
|
|
|
|
|
def string_to_bools(message):
|
|
|
|
# For easter eggs on the board
|
|
|
|
bits = bin(int.from_bytes(message.encode(), 'big'))[2:]
|
|
|
|
bits = (len(message) * 8 - len(bits)) * '0' + bits
|
|
|
|
return [bool(int(b)) for b in bits]
|
|
|
|
|
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
def layer_mobject(mobject, nudge=1e-6):
|
|
|
|
for i, sm in enumerate(mobject.family_members_with_points()):
|
|
|
|
sm.shift(i * nudge * OUT)
|
|
|
|
|
|
|
|
|
2020-06-07 12:26:31 -07:00
|
|
|
class Chessboard(SGroup):
|
|
|
|
CONFIG = {
|
|
|
|
"shape": (8, 8),
|
|
|
|
"height": 7,
|
|
|
|
"depth": 0.25,
|
2020-06-13 15:19:42 -07:00
|
|
|
"colors": [LIGHT_GREY, DARKER_GREY],
|
|
|
|
"gloss": 0.2,
|
2020-06-17 17:13:26 -07:00
|
|
|
"square_resolution": (3, 3),
|
|
|
|
"top_square_resolution": (5, 5),
|
2020-06-07 12:26:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
nr, nc = self.shape
|
2020-06-17 17:13:26 -07:00
|
|
|
cube = Cube(square_resolution=self.square_resolution)
|
2020-06-07 12:26:31 -07:00
|
|
|
# Replace top square with something slightly higher res
|
2020-06-17 17:13:26 -07:00
|
|
|
top_square = Square3D(resolution=self.top_square_resolution)
|
2020-06-07 12:26:31 -07:00
|
|
|
top_square.replace(cube[0])
|
|
|
|
cube.replace_submobject(0, top_square)
|
|
|
|
self.add(*[cube.copy() for x in range(nc * nr)])
|
|
|
|
self.arrange_in_grid(buff=0)
|
|
|
|
self.set_height(self.height)
|
|
|
|
self.set_depth(self.depth, stretch=True)
|
|
|
|
for i, j in it.product(range(nr), range(nc)):
|
|
|
|
color = self.colors[(i + j) % 2]
|
|
|
|
self[i * nc + j].set_color(color)
|
|
|
|
self.center()
|
2020-06-13 15:19:42 -07:00
|
|
|
self.set_gloss(self.gloss)
|
2020-06-07 12:26:31 -07:00
|
|
|
|
|
|
|
|
|
|
|
class Coin(Group):
|
|
|
|
CONFIG = {
|
2020-06-13 15:19:42 -07:00
|
|
|
"disk_resolution": (4, 51),
|
2020-06-07 12:26:31 -07:00
|
|
|
"height": 1,
|
|
|
|
"depth": 0.1,
|
|
|
|
"color": GOLD_D,
|
|
|
|
"tails_color": RED,
|
|
|
|
"include_labels": True,
|
2020-06-17 17:13:26 -07:00
|
|
|
"numeric_labels": False,
|
2020-06-07 12:26:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
2020-06-13 15:19:42 -07:00
|
|
|
res = self.disk_resolution
|
|
|
|
self.top = Disk3D(resolution=res, gloss=0.2)
|
|
|
|
self.bottom = self.top.copy()
|
|
|
|
self.top.shift(OUT)
|
|
|
|
self.bottom.shift(IN)
|
|
|
|
self.edge = Cylinder(height=2, resolution=(res[1], 2))
|
2020-06-07 12:26:31 -07:00
|
|
|
self.add(self.top, self.bottom, self.edge)
|
2020-06-13 15:19:42 -07:00
|
|
|
self.rotate(90 * DEGREES, OUT)
|
2020-06-07 12:26:31 -07:00
|
|
|
self.set_color(self.color)
|
|
|
|
self.bottom.set_color(RED)
|
|
|
|
|
|
|
|
if self.include_labels:
|
2020-06-17 17:13:26 -07:00
|
|
|
chars = "10" if self.numeric_labels else "HT"
|
|
|
|
labels = VGroup(*[TextMobject(c) for c in chars])
|
2020-06-07 12:26:31 -07:00
|
|
|
for label, vect in zip(labels, [OUT, IN]):
|
2020-06-15 12:03:23 -07:00
|
|
|
label.shift(1.02 * vect)
|
2020-06-07 12:26:31 -07:00
|
|
|
label.set_height(0.8)
|
2020-06-17 17:13:26 -07:00
|
|
|
labels[1].rotate(PI, RIGHT)
|
2020-06-15 12:03:23 -07:00
|
|
|
labels.apply_depth_test()
|
2020-06-17 17:13:26 -07:00
|
|
|
self.add(*labels)
|
|
|
|
self.labels = labels
|
2020-06-07 12:26:31 -07:00
|
|
|
|
|
|
|
self.set_height(self.height)
|
|
|
|
self.set_depth(self.depth, stretch=True)
|
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
def is_heads(self):
|
|
|
|
return self.top.get_center()[2] > self.bottom.get_center()[2]
|
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
def flip(self, axis=RIGHT):
|
|
|
|
super().flip(axis)
|
|
|
|
|
2020-06-07 12:26:31 -07:00
|
|
|
|
|
|
|
class CoinsOnBoard(Group):
|
|
|
|
CONFIG = {
|
|
|
|
"proportion_of_square_height": 0.7,
|
|
|
|
"coin_config": {},
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, chessboard, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
prop = self.proportion_of_square_height
|
|
|
|
for cube in chessboard:
|
|
|
|
coin = Coin(**self.coin_config)
|
|
|
|
coin.set_height(prop * cube.get_height())
|
|
|
|
coin.next_to(cube, OUT, buff=0)
|
|
|
|
self.add(coin)
|
|
|
|
|
|
|
|
def flip_at_random(self, p=0.5):
|
2020-06-17 17:13:26 -07:00
|
|
|
bools = np.random.random(len(self)) < p
|
|
|
|
self.flip_by_bools(bools)
|
2020-06-15 12:03:23 -07:00
|
|
|
return self
|
|
|
|
|
|
|
|
def flip_by_message(self, message):
|
2020-06-17 17:13:26 -07:00
|
|
|
self.flip_by_bools(string_to_bools(message))
|
|
|
|
return self
|
|
|
|
|
|
|
|
def flip_by_bools(self, bools):
|
|
|
|
for coin, head in zip(self, bools):
|
|
|
|
if coin.is_heads() ^ head:
|
2020-06-15 12:03:23 -07:00
|
|
|
coin.flip()
|
2020-06-07 12:26:31 -07:00
|
|
|
return self
|
|
|
|
|
2020-06-17 17:13:26 -07:00
|
|
|
def get_bools(self):
|
|
|
|
return [coin.is_heads() for coin in self]
|
|
|
|
|
2020-06-07 12:26:31 -07:00
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
class Key(SVGMobject):
|
|
|
|
CONFIG = {
|
|
|
|
"file_name": "key",
|
2020-06-17 17:13:26 -07:00
|
|
|
"fill_color": YELLOW_D,
|
2020-06-15 12:03:23 -07:00
|
|
|
"fill_opacity": 1,
|
2020-06-17 17:13:26 -07:00
|
|
|
"stroke_color": YELLOW_D,
|
2020-06-15 12:03:23 -07:00
|
|
|
"stroke_width": 0,
|
|
|
|
"gloss": 0.5,
|
2020-06-17 17:13:26 -07:00
|
|
|
"depth_test": True,
|
2020-06-15 12:03:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super().__init__(**kwargs)
|
|
|
|
self.rotate(PI / 2, OUT)
|
|
|
|
|
|
|
|
|
2020-06-10 07:28:43 -07:00
|
|
|
class FlipCoin(Animation):
|
|
|
|
CONFIG = {
|
|
|
|
"axis": RIGHT,
|
|
|
|
"run_time": 1,
|
|
|
|
"shift_dir": OUT,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, coin, **kwargs):
|
|
|
|
super().__init__(coin, **kwargs)
|
|
|
|
self.shift_vect = coin.get_height() * self.shift_dir / 2
|
|
|
|
|
|
|
|
def interpolate_mobject(self, alpha):
|
|
|
|
coin = self.mobject
|
|
|
|
for sm, start_sm in self.families:
|
|
|
|
sm.points[:] = start_sm.points
|
|
|
|
coin.rotate(alpha * PI, axis=self.axis)
|
|
|
|
coin.shift(4 * alpha * (1 - alpha) * self.shift_vect)
|
|
|
|
return coin
|
|
|
|
|
2020-06-07 12:26:31 -07:00
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
# Scenes
|
2020-06-10 07:28:43 -07:00
|
|
|
class IntroducePuzzle(Scene):
|
2020-06-05 16:33:30 -07:00
|
|
|
CONFIG = {
|
2020-06-13 15:19:42 -07:00
|
|
|
"camera_class": ThreeDCamera,
|
2020-06-05 16:33:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
def construct(self):
|
2020-06-13 15:19:42 -07:00
|
|
|
# Setup
|
2020-06-10 07:28:43 -07:00
|
|
|
frame = self.camera.frame
|
|
|
|
|
2020-06-07 12:26:31 -07:00
|
|
|
chessboard = Chessboard()
|
2020-06-05 16:33:30 -07:00
|
|
|
chessboard.move_to(ORIGIN, OUT)
|
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
grid = NumberPlane(
|
2020-06-10 07:28:43 -07:00
|
|
|
x_range=(0, 8), y_range=(0, 8),
|
2020-06-13 15:19:42 -07:00
|
|
|
faded_line_ratio=0
|
2020-06-10 07:28:43 -07:00
|
|
|
)
|
2020-06-13 15:19:42 -07:00
|
|
|
grid.match_height(chessboard)
|
|
|
|
grid.next_to(chessboard, OUT, 1e-8)
|
|
|
|
low_grid = grid.copy()
|
|
|
|
low_grid.next_to(chessboard, IN, 1e-8)
|
|
|
|
grid.add(low_grid)
|
|
|
|
grid.set_stroke(GREY, width=2)
|
|
|
|
grid.set_gloss(0.5)
|
|
|
|
grid.prepare_for_nonlinear_transform(0)
|
2020-06-10 07:28:43 -07:00
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
coins = CoinsOnBoard(chessboard)
|
2020-06-13 15:19:42 -07:00
|
|
|
coins.set_gloss(0.2)
|
|
|
|
coins_to_flip = Group()
|
|
|
|
head_bools = string_to_bools('3b1b :)')
|
|
|
|
for coin, heads in zip(coins, head_bools):
|
|
|
|
if not heads:
|
|
|
|
coins_to_flip.add(coin)
|
|
|
|
coins_to_flip.shuffle()
|
|
|
|
|
|
|
|
count_label = VGroup(
|
|
|
|
Integer(0, edge_to_fix=RIGHT),
|
|
|
|
TextMobject("Coins")
|
|
|
|
)
|
|
|
|
count_label.arrange(RIGHT, aligned_edge=DOWN)
|
|
|
|
count_label.to_corner(UL)
|
|
|
|
count_label.fix_in_frame()
|
2020-06-05 16:33:30 -07:00
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
# Draw board and coins
|
|
|
|
frame.set_rotation(-25 * DEGREES, 70 * DEGREES, 0)
|
|
|
|
self.play(
|
|
|
|
FadeIn(chessboard),
|
|
|
|
ShowCreationThenDestruction(grid, lag_ratio=0.01),
|
|
|
|
frame.set_theta, 0,
|
|
|
|
frame.set_phi, 45 * DEGREES,
|
|
|
|
run_time=3,
|
|
|
|
)
|
|
|
|
self.wait()
|
2020-06-05 16:33:30 -07:00
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
self.add(count_label)
|
|
|
|
self.play(
|
|
|
|
ShowIncreasingSubsets(coins),
|
|
|
|
UpdateFromFunc(count_label[0], lambda m, c=coins: m.set_value(len(c))),
|
|
|
|
rate_func=bezier([0, 0, 1, 1]),
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(LaggedStartMap(FlipCoin, coins_to_flip, run_time=6, lag_ratio=0.1))
|
2020-06-05 16:33:30 -07:00
|
|
|
self.add(coins)
|
2020-06-13 15:19:42 -07:00
|
|
|
coins.unlock_shader_data()
|
2020-06-10 07:28:43 -07:00
|
|
|
self.wait()
|
2020-06-07 12:26:31 -07:00
|
|
|
|
2020-06-13 15:19:42 -07:00
|
|
|
# Show key
|
2020-06-15 12:03:23 -07:00
|
|
|
key = Key()
|
2020-06-13 15:19:42 -07:00
|
|
|
key.rotate(PI / 4, RIGHT)
|
|
|
|
key.move_to(3 * OUT)
|
|
|
|
key.scale(0.8)
|
|
|
|
key.to_edge(LEFT, buff=1)
|
|
|
|
|
|
|
|
k = boolian_linear_combo(head_bools) ^ 63 # To make the flip below the actual solution
|
|
|
|
key_cube = Cube(resolution=(6, 6))
|
|
|
|
key_cube.match_color(chessboard[k])
|
|
|
|
key_cube.replace(chessboard[k], stretch=True)
|
|
|
|
chessboard.replace_submobject(k, key_cube)
|
|
|
|
key_square = key_cube[0]
|
|
|
|
chessboard.generate_target()
|
|
|
|
chessboard.save_state()
|
|
|
|
for i, cube in enumerate(chessboard.target):
|
|
|
|
if i == k:
|
|
|
|
cube[0].set_color(MAROON_E)
|
|
|
|
else:
|
|
|
|
cube.set_color(interpolate_color(cube.get_color(), BLACK, 0.8))
|
|
|
|
|
|
|
|
key.generate_target()
|
|
|
|
key.target.rotate(PI / 4, LEFT)
|
|
|
|
key.target.set_width(0.7 * key_square.get_width())
|
|
|
|
key.target.next_to(key_square, IN, buff=SMALL_BUFF)
|
|
|
|
|
|
|
|
self.play(FadeIn(key, LEFT))
|
2020-06-07 12:26:31 -07:00
|
|
|
self.wait()
|
2020-06-13 15:19:42 -07:00
|
|
|
self.play(
|
|
|
|
FadeOut(coins, lag_ratio=0.01),
|
|
|
|
MoveToTarget(chessboard),
|
|
|
|
)
|
|
|
|
ks_top = key_square.get_top()
|
|
|
|
self.play(
|
|
|
|
Rotate(key_square, PI / 2, axis=LEFT, about_point=ks_top),
|
|
|
|
MoveToTarget(key),
|
|
|
|
frame.set_phi, 60 * DEGREES,
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Rotate(key_square, PI / 2, axis=RIGHT, about_point=ks_top),
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
chessboard.unlock_shader_data()
|
|
|
|
chessboard.saved_state[k][0].match_color(key_square)
|
|
|
|
self.play(
|
|
|
|
chessboard.restore,
|
|
|
|
FadeIn(coins),
|
|
|
|
frame.set_phi, 20 * DEGREES,
|
|
|
|
frame.move_to, 2 * LEFT,
|
|
|
|
run_time=3
|
|
|
|
)
|
|
|
|
|
|
|
|
# State goal
|
|
|
|
goal = TextMobject(
|
|
|
|
"Communicate where\\\\the key is",
|
|
|
|
" by turning\\\\over one coin.",
|
|
|
|
alignment=""
|
|
|
|
)
|
|
|
|
goal.next_to(count_label, DOWN, LARGE_BUFF, LEFT)
|
|
|
|
goal.fix_in_frame()
|
|
|
|
goal[1].set_color(YELLOW)
|
|
|
|
|
|
|
|
self.play(FadeIn(goal[0]))
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeIn(goal[1]))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
coin = coins[63]
|
|
|
|
rect = SurroundingRectangle(coin, color=TEAL)
|
|
|
|
|
|
|
|
self.play(FadeInFromLarge(rect))
|
|
|
|
self.play(FlipCoin(coin), FadeOut(rect))
|
|
|
|
|
|
|
|
|
|
|
|
class FromCoinToSquareMaps(Scene):
|
|
|
|
CONFIG = {
|
|
|
|
"camera_class": ThreeDCamera,
|
|
|
|
"messages": [
|
|
|
|
"Please, ",
|
|
|
|
"go watch",
|
|
|
|
"Stand-up",
|
|
|
|
"Maths on",
|
|
|
|
"YouTube."
|
|
|
|
],
|
|
|
|
"flip_lag_ratio": 0.05,
|
|
|
|
}
|
|
|
|
|
|
|
|
def construct(self):
|
|
|
|
messages = self.messages
|
|
|
|
|
|
|
|
board1 = Chessboard()
|
|
|
|
board1.set_width(5.5)
|
|
|
|
board1.to_corner(DL)
|
|
|
|
|
|
|
|
board2 = board1.copy()
|
|
|
|
board2.to_corner(DR)
|
|
|
|
|
|
|
|
coins = CoinsOnBoard(board1)
|
|
|
|
bools = string_to_bools(messages[0])
|
|
|
|
for coin, head in zip(coins, bools):
|
|
|
|
if not head:
|
|
|
|
coin.flip(RIGHT)
|
|
|
|
|
|
|
|
for cube in board2:
|
|
|
|
cube.original_color = cube.get_color()
|
|
|
|
|
|
|
|
arrow = Arrow(board1.get_right(), board2.get_left())
|
|
|
|
arrow.tip.set_stroke(width=0)
|
|
|
|
|
|
|
|
title1 = TextMobject("Pattern of coins")
|
|
|
|
title2 = TextMobject("Individual square")
|
|
|
|
|
|
|
|
for title, board in [(title1, board1), (title2, board2)]:
|
|
|
|
title.scale(0.5 / title[0][0].get_height())
|
|
|
|
title.next_to(board, UP, MED_LARGE_BUFF)
|
|
|
|
|
|
|
|
title2.align_to(title1, UP)
|
|
|
|
|
|
|
|
def get_special_square(coins=coins, board=board2):
|
|
|
|
bools = [coin.is_heads() for coin in coins]
|
|
|
|
return board[boolian_linear_combo(bools)]
|
|
|
|
|
|
|
|
self.add(board1)
|
|
|
|
self.add(title1)
|
|
|
|
self.add(coins)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
GrowArrow(arrow),
|
|
|
|
FadeIn(board2, 2 * LEFT)
|
|
|
|
)
|
|
|
|
square = get_special_square()
|
|
|
|
rect = SurroundingRectangle(square, buff=0)
|
|
|
|
rect.set_color(PINK)
|
|
|
|
rect.next_to(square, OUT, buff=0.01)
|
|
|
|
self.play(
|
|
|
|
square.set_color, MAROON_C,
|
|
|
|
ShowCreation(rect),
|
|
|
|
FadeIn(title2)
|
|
|
|
)
|
|
|
|
|
|
|
|
for message in messages[1:]:
|
|
|
|
new_bools = string_to_bools(message)
|
|
|
|
coins_to_flip = Group()
|
|
|
|
for coin, to_heads in zip(coins, new_bools):
|
|
|
|
if coin.is_heads() ^ to_heads:
|
|
|
|
coins_to_flip.add(coin)
|
|
|
|
coins_to_flip.shuffle()
|
|
|
|
self.play(LaggedStartMap(
|
|
|
|
FlipCoin, coins_to_flip,
|
|
|
|
lag_ratio=self.flip_lag_ratio,
|
|
|
|
run_time=1,
|
|
|
|
))
|
|
|
|
|
|
|
|
new_square = get_special_square()
|
|
|
|
self.play(
|
|
|
|
square.set_color, square.original_color,
|
|
|
|
new_square.set_color, MAROON_C,
|
|
|
|
rect.move_to, new_square, OUT,
|
|
|
|
rect.shift, 0.01 * OUT,
|
|
|
|
)
|
|
|
|
square = new_square
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class FromCoinToSquareMapsSingleFlips(FromCoinToSquareMaps):
|
|
|
|
CONFIG = {
|
|
|
|
"messages": [
|
|
|
|
"FlipBits",
|
|
|
|
"BlipBits",
|
|
|
|
"ClipBits",
|
|
|
|
"ChipBits",
|
|
|
|
"ChipBats",
|
|
|
|
"ChipRats",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
class DiagramOfProgression(ThreeDScene):
|
2020-06-13 15:19:42 -07:00
|
|
|
def construct(self):
|
2020-06-15 12:03:23 -07:00
|
|
|
# Setup panels
|
|
|
|
P1_COLOR = BLUE_C
|
|
|
|
P2_COLOR = RED
|
|
|
|
|
|
|
|
rect = Rectangle(4, 2)
|
|
|
|
rect.set_fill(GREY_E, 1)
|
|
|
|
panels = Group()
|
|
|
|
for x in range(4):
|
|
|
|
panels.add(Group(rect.copy()))
|
|
|
|
panels.arrange_in_grid(buff=1)
|
|
|
|
panels[::2].shift(0.5 * LEFT)
|
|
|
|
panels.set_width(FRAME_WIDTH - 2)
|
|
|
|
panels.center().to_edge(DOWN)
|
2020-06-13 15:19:42 -07:00
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
chessboard = Chessboard()
|
|
|
|
chessboard.set_height(0.9 * panels[0].get_height())
|
|
|
|
coins = CoinsOnBoard(
|
|
|
|
chessboard,
|
|
|
|
coin_config={
|
|
|
|
"disk_resolution": (2, 25),
|
2020-06-17 17:13:26 -07:00
|
|
|
# "include_labels": False,
|
2020-06-15 12:03:23 -07:00
|
|
|
}
|
|
|
|
)
|
|
|
|
coins.flip_by_message("Tau > Pi")
|
|
|
|
|
|
|
|
for panel in panels[1:]:
|
|
|
|
cb = chessboard.copy()
|
|
|
|
co = coins.copy()
|
|
|
|
cb.next_to(panel.get_right(), LEFT)
|
|
|
|
co.next_to(cb, OUT, 0)
|
|
|
|
panel.chessboard = cb
|
|
|
|
panel.coins = co
|
|
|
|
panel.add(cb, co)
|
|
|
|
|
|
|
|
kw = {
|
|
|
|
"tex_to_color_map": {
|
|
|
|
"Prisoner 1": P1_COLOR,
|
|
|
|
"Prisoner 2": P2_COLOR,
|
|
|
|
}
|
|
|
|
}
|
2020-06-13 15:19:42 -07:00
|
|
|
titles = VGroup(
|
2020-06-15 12:03:23 -07:00
|
|
|
TextMobject("Prisoners conspire", **kw),
|
|
|
|
TextMobject("Prisoner 1 sees key", **kw),
|
|
|
|
TextMobject("Prisoner 1 flips coin", **kw),
|
|
|
|
TextMobject("Prisoner 2 guesses key square", **kw),
|
|
|
|
)
|
|
|
|
|
|
|
|
for panel, title in zip(panels, titles):
|
|
|
|
title.next_to(panel, UP)
|
|
|
|
panel.title = title
|
|
|
|
panel.add(title)
|
|
|
|
|
|
|
|
# Darken first chessboard
|
|
|
|
for coin in panels[1].coins:
|
|
|
|
coin.remove(coin.edge)
|
|
|
|
if coin.is_heads():
|
|
|
|
coin.remove(coin.bottom)
|
|
|
|
coin.remove(coin.labels[1])
|
|
|
|
else:
|
|
|
|
coin.remove(coin.top)
|
|
|
|
coin.remove(coin.labels[0])
|
|
|
|
coin.set_opacity(0.25)
|
|
|
|
|
|
|
|
# Add characters
|
|
|
|
prisoner1 = PiCreature(color=P1_COLOR)
|
|
|
|
prisoner2 = PiCreature(color=P2_COLOR)
|
|
|
|
pis = VGroup(prisoner1, prisoner2)
|
|
|
|
pis.arrange(RIGHT, buff=1)
|
|
|
|
pis.set_height(1.5)
|
|
|
|
|
|
|
|
p0_pis = pis.copy()
|
|
|
|
p0_pis.set_height(2.0, about_edge=DOWN)
|
|
|
|
p0_pis[1].flip()
|
|
|
|
p0_pis.next_to(panels[0].get_bottom(), UP, SMALL_BUFF)
|
|
|
|
p0_pis[0].change("pondering", p0_pis[1].eyes)
|
|
|
|
p0_pis[1].change("speaking", p0_pis[0].eyes)
|
|
|
|
panels[0].add(p0_pis)
|
|
|
|
|
|
|
|
p1_pi = pis[0].copy()
|
|
|
|
p1_pi.next_to(panels[1].get_corner(DL), UR, SMALL_BUFF)
|
|
|
|
p1_pi.change("happy")
|
|
|
|
key = Key()
|
|
|
|
key.set_height(0.5)
|
|
|
|
key.next_to(p1_pi, UP)
|
|
|
|
key.set_color(YELLOW)
|
|
|
|
key_cube = panels[1].chessboard[18]
|
|
|
|
key_square = Square()
|
|
|
|
key_square.replace(key_cube)
|
|
|
|
key_square.set_stroke(width=3)
|
|
|
|
key_square.match_color(key)
|
|
|
|
p1_pi.look_at(key_square)
|
|
|
|
key_arrow = Arrow(
|
|
|
|
key.get_right() + SMALL_BUFF * UP,
|
|
|
|
key_square.get_corner(UL),
|
|
|
|
path_arc=-45 * DEGREES,
|
|
|
|
buff=SMALL_BUFF
|
|
|
|
)
|
|
|
|
key_arrow.tip.set_stroke(width=0)
|
|
|
|
key_arrow.match_color(key)
|
|
|
|
panels[1].add(p1_pi, key)
|
|
|
|
|
|
|
|
p2_pi = pis[0].copy()
|
|
|
|
p2_pi.next_to(panels[2].get_corner(DL), UR, SMALL_BUFF)
|
|
|
|
p2_pi.change("tease")
|
|
|
|
flip_coin = panels[2].coins[38]
|
|
|
|
panels[3].coins[38].flip()
|
|
|
|
flip_square = Square()
|
|
|
|
flip_square.replace(panels[2].chessboard[38])
|
|
|
|
flip_square.set_stroke(BLUE, 5)
|
|
|
|
for coin in panels[2].coins:
|
|
|
|
if coin is not flip_coin:
|
|
|
|
coin.remove(coin.edge)
|
|
|
|
if coin.is_heads():
|
|
|
|
coin.remove(coin.bottom)
|
|
|
|
coin.remove(coin.labels[1])
|
|
|
|
else:
|
|
|
|
coin.remove(coin.top)
|
|
|
|
coin.remove(coin.labels[0])
|
|
|
|
coin.set_opacity(0.25)
|
|
|
|
panels[2].add(p2_pi)
|
|
|
|
|
|
|
|
p3_pi = pis[1].copy()
|
|
|
|
p3_pi.next_to(panels[3].get_corner(DL), UR, SMALL_BUFF)
|
|
|
|
p3_pi.shift(MED_LARGE_BUFF * RIGHT)
|
|
|
|
p3_pi.change("confused")
|
|
|
|
panels[3].add(p3_pi)
|
|
|
|
|
|
|
|
# Animate each panel in
|
|
|
|
self.play(FadeIn(panels[1], DOWN))
|
|
|
|
self.play(
|
|
|
|
ShowCreation(key_arrow),
|
|
|
|
FadeInFromLarge(key_square),
|
2020-06-13 15:19:42 -07:00
|
|
|
)
|
2020-06-15 12:03:23 -07:00
|
|
|
self.wait()
|
2020-06-13 15:19:42 -07:00
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
self.play(FadeIn(panels[2], UP))
|
|
|
|
self.play(
|
|
|
|
ShowCreation(flip_square),
|
|
|
|
FlipCoin(flip_coin),
|
|
|
|
p2_pi.look_at, flip_coin,
|
|
|
|
)
|
|
|
|
self.wait()
|
2020-06-10 07:28:43 -07:00
|
|
|
|
2020-06-15 12:03:23 -07:00
|
|
|
self.play(FadeIn(panels[3], LEFT))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
self.play(FadeIn(panels[0]))
|
|
|
|
self.wait()
|
2020-06-17 17:13:26 -07:00
|
|
|
|
|
|
|
|
|
|
|
class SlightTweakIsImpossible(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# Definitions
|
|
|
|
frame = self.camera.frame
|
|
|
|
title = TextMobject("Describe any square\\\\with one flip")
|
|
|
|
title.set_height(1.2)
|
|
|
|
title.to_edge(UP)
|
|
|
|
title.fix_in_frame()
|
|
|
|
|
|
|
|
left_board = Chessboard()
|
|
|
|
right_board = Chessboard()
|
|
|
|
for board, vect in (left_board, LEFT), (right_board, RIGHT):
|
|
|
|
board.set_width(4.5)
|
|
|
|
board.to_corner(DOWN + vect, buff=LARGE_BUFF)
|
|
|
|
coins = CoinsOnBoard(left_board)
|
|
|
|
coins.flip_by_message("HammCode")
|
|
|
|
|
|
|
|
arrow = Arrow(left_board.get_right(), right_board.get_left())
|
|
|
|
|
|
|
|
# Prepare for colorings
|
|
|
|
for cube in right_board:
|
|
|
|
cube.original_color = cube.get_color()
|
|
|
|
|
|
|
|
def get_special_square(board=right_board, coins=coins):
|
|
|
|
return board[boolian_linear_combo(coins.get_bools())]
|
|
|
|
|
|
|
|
frame.set_phi(45 * DEGREES)
|
|
|
|
|
|
|
|
# Introduce boards
|
|
|
|
self.add(title)
|
|
|
|
group = Group(*left_board, *coins, *right_board)
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(FadeInFromLarge, group, lambda m: (m, 1.3), lag_ratio=0.01),
|
|
|
|
GrowArrow(arrow)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Flip one at a time
|
|
|
|
self.colored_square = right_board[0]
|
|
|
|
self.random_flips(3, left_board, coins, get_special_square)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Remove a square
|
|
|
|
to_remove = Group(
|
|
|
|
left_board[63], right_board[63], coins[63]
|
|
|
|
)
|
|
|
|
remove_words = TextMobject("Remove one\\\\square")
|
|
|
|
remove_words.set_color(RED)
|
|
|
|
remove_words.to_corner(DOWN, buff=1.5)
|
|
|
|
remove_words.fix_in_frame()
|
|
|
|
|
|
|
|
self.play(FadeIn(remove_words, DOWN))
|
|
|
|
self.play(FadeOut(to_remove, 3 * IN))
|
|
|
|
|
|
|
|
# Not possible
|
|
|
|
cross = Cross(title)
|
|
|
|
cross.fix_in_frame()
|
|
|
|
cross.set_stroke(RED, 8)
|
|
|
|
imp_words = TextMobject("Impossible!")
|
|
|
|
imp_words.fix_in_frame()
|
|
|
|
imp_words.next_to(title, RIGHT, buff=1.5)
|
|
|
|
imp_words.shift(2 * LEFT)
|
|
|
|
imp_words.set_height(0.7)
|
|
|
|
imp_words.set_color(RED)
|
|
|
|
|
|
|
|
self.play(ShowCreation(cross))
|
|
|
|
self.play(
|
|
|
|
title.shift, 2 * LEFT,
|
|
|
|
cross.shift, 2 * LEFT,
|
|
|
|
FadeIn(imp_words, LEFT)
|
|
|
|
)
|
|
|
|
|
|
|
|
# More flips
|
|
|
|
self.random_flips(5, left_board, coins, get_special_square)
|
|
|
|
|
|
|
|
def random_flips(self, n, left_board, coins, get_special_square):
|
|
|
|
for x in range(n):
|
|
|
|
n = random.randint(0, len(coins) - 1)
|
|
|
|
pre_square = Square()
|
|
|
|
pre_square.replace(left_board)
|
|
|
|
pre_square.move_to(left_board, OUT)
|
|
|
|
pre_square.set_stroke(BLUE, 3)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[n]),
|
|
|
|
FadeIn(pre_square)
|
|
|
|
)
|
|
|
|
new_colored_square = get_special_square()
|
|
|
|
pre_square.generate_target()
|
|
|
|
pre_square.target.replace(new_colored_square[0])
|
|
|
|
pre_square.target.set_fill(BLUE, 0.5)
|
|
|
|
pre_square.target.set_stroke(width=0)
|
|
|
|
self.play(
|
|
|
|
new_colored_square.set_color, BLUE,
|
|
|
|
self.colored_square.set_color, self.colored_square.original_color,
|
|
|
|
MoveToTarget(pre_square, remover=True)
|
|
|
|
)
|
|
|
|
self.colored_square = new_colored_square
|
|
|
|
|
|
|
|
|
|
|
|
class TwoSquareCase(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
frame = self.camera.frame
|
|
|
|
|
|
|
|
# Transition to just two square
|
|
|
|
chessboard = Chessboard()
|
|
|
|
coins = CoinsOnBoard(chessboard)
|
|
|
|
coins.flip_by_message("To 2 bits")
|
|
|
|
|
|
|
|
to_remove = Group(*it.chain(*zip(chessboard[:1:-1], coins[:1:-1])))
|
|
|
|
small_board = chessboard[:2]
|
|
|
|
coin_pair = coins[:2]
|
|
|
|
small_group = Group(small_board, coin_pair)
|
|
|
|
|
|
|
|
frame.set_phi(45 * DEGREES)
|
|
|
|
|
|
|
|
two_square_words = TextMobject("What about a 2-square board?")
|
|
|
|
two_square_words.fix_in_frame()
|
|
|
|
two_square_words.set_height(0.5)
|
|
|
|
two_square_words.center().to_edge(UP)
|
|
|
|
|
|
|
|
self.add(chessboard, coins)
|
|
|
|
self.play(
|
|
|
|
Write(two_square_words, run_time=1),
|
|
|
|
LaggedStartMap(
|
|
|
|
FadeOut, to_remove,
|
|
|
|
lambda m: (m, DOWN),
|
|
|
|
run_time=3,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
small_group.center,
|
|
|
|
small_group.set_height, 1.5,
|
|
|
|
frame.set_phi, 10 * DEGREES,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
coins = coin_pair
|
|
|
|
|
|
|
|
# Show two locations for the key
|
|
|
|
key = Key()
|
|
|
|
key.set_width(0.7 * small_board[0].get_width())
|
|
|
|
key.move_to(small_board[0])
|
|
|
|
key.shift(0.01 * OUT)
|
|
|
|
|
|
|
|
coin_pair.shift(0.04 * OUT)
|
|
|
|
s0_top = small_board[0][0].get_top()
|
|
|
|
s1_top = small_board[1][0].get_top()
|
|
|
|
|
|
|
|
s0_rot_group = Group(small_board[0][0], coin_pair[0])
|
|
|
|
s1_rot_group = Group(small_board[1][0], coin_pair[1])
|
|
|
|
|
|
|
|
self.add(key)
|
|
|
|
angle = 170 * DEGREES
|
|
|
|
self.play(
|
|
|
|
Rotate(s0_rot_group, angle, LEFT, about_point=s0_top),
|
|
|
|
Rotate(s1_rot_group, angle, LEFT, about_point=s1_top),
|
|
|
|
frame.set_phi, 45 * DEGREES,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
key.match_x, small_board[1],
|
|
|
|
path_arc=PI,
|
|
|
|
path_arc_axis=UP,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
Rotate(s0_rot_group, angle, RIGHT, about_point=s0_top),
|
|
|
|
Rotate(s1_rot_group, angle, RIGHT, about_point=s1_top),
|
|
|
|
frame.set_phi, 0,
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show four states pointing to two message
|
|
|
|
states = VGroup(*[
|
|
|
|
TextMobject(letters, tex_to_color_map={"H": GOLD, "T": RED_D})
|
|
|
|
for letters in ["TT", "HT", "TH", "HH"]
|
|
|
|
])
|
|
|
|
states.set_height(0.8)
|
|
|
|
states.arrange(DOWN, buff=1)
|
|
|
|
states.to_edge(LEFT)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(two_square_words),
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
FadeIn(states[0])
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
FadeIn(states[1])
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
FadeIn(states[2])
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
FadeIn(states[3])
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
key_copy = key.copy()
|
|
|
|
key_copy.match_x(small_board[0])
|
|
|
|
small_board_copy = small_board.copy()
|
|
|
|
small_boards = Group(small_board_copy, small_board)
|
|
|
|
for board, vect in zip(small_boards, [UP, DOWN]):
|
|
|
|
board.generate_target()
|
|
|
|
board.target.set_opacity(0.7)
|
|
|
|
board.target.shift(2 * vect)
|
|
|
|
board.target.set_depth(0.01, stretch=True)
|
|
|
|
self.add(key, key_copy, *small_boards)
|
|
|
|
self.play(
|
|
|
|
FadeOut(coins),
|
|
|
|
MoveToTarget(small_board),
|
|
|
|
MaintainPositionRelativeTo(key, small_board),
|
|
|
|
MoveToTarget(small_board_copy),
|
|
|
|
MaintainPositionRelativeTo(key_copy, small_board_copy),
|
|
|
|
)
|
|
|
|
self.add(*small_boards, key, key_copy)
|
|
|
|
|
|
|
|
arrows = VGroup()
|
|
|
|
for i in range(4):
|
|
|
|
arrows.add(Arrow(states[i].get_right(), small_boards[i // 2].get_left()))
|
|
|
|
arrows.set_opacity(0.75)
|
|
|
|
|
|
|
|
self.play(LaggedStartMap(GrowArrow, arrows, lag_ratio=0.3))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show one flip changing interpretation
|
|
|
|
coins.next_to(states, LEFT, buff=1.5)
|
|
|
|
|
|
|
|
def get_state(coins=coins):
|
|
|
|
bools = [c.is_heads() for c in coins]
|
|
|
|
return sum([b * (2**n) for n, b in enumerate(reversed(bools))])
|
|
|
|
|
|
|
|
n = 3
|
|
|
|
state_rect = SurroundingRectangle(states[n])
|
|
|
|
board_rects = VGroup()
|
|
|
|
for board in small_boards:
|
|
|
|
br = SurroundingRectangle(board, buff=0)
|
|
|
|
br.move_to(board, OUT)
|
|
|
|
br.set_stroke(YELLOW, 3)
|
|
|
|
board_rects.add(br)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ApplyMethod(frame.shift, 4.5 * LEFT, run_time=1),
|
|
|
|
FadeIn(coins),
|
|
|
|
FadeIn(state_rect),
|
|
|
|
FadeIn(board_rects[1]),
|
|
|
|
arrows[n].set_color, YELLOW,
|
|
|
|
arrows[n].set_opacity, 1,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
FadeOut(board_rects[1]),
|
|
|
|
FadeIn(board_rects[0]),
|
|
|
|
state_rect.move_to, states[1],
|
|
|
|
arrows[3].match_style, arrows[0],
|
|
|
|
arrows[1].match_style, arrows[3],
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Erase H and T, replace with 1 and 0
|
|
|
|
bin_states = VGroup(*[
|
|
|
|
TextMobject(letters, tex_to_color_map={"1": GOLD, "0": RED_D})
|
|
|
|
for letters in ["00", "10", "01", "11"]
|
|
|
|
])
|
|
|
|
for bin_state, state in zip(bin_states, states):
|
|
|
|
for bit, letter in zip(bin_state, state):
|
|
|
|
bit.replace(letter, dim_to_match=1)
|
|
|
|
bin_coins = CoinsOnBoard(small_board, coin_config={"numeric_labels": True})
|
|
|
|
bin_coins[1].flip()
|
|
|
|
bin_coins.move_to(coins)
|
|
|
|
|
|
|
|
self.remove(coins)
|
|
|
|
self.add(bin_coins)
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(GrowFromCenter, Group(*bin_states.family_members_with_points())),
|
|
|
|
LaggedStartMap(ApplyMethod, Group(*states.family_members_with_points()), lambda m: (m.scale, 0)),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Add labels
|
|
|
|
c_labels = VGroup(*[
|
|
|
|
TexMobject(f"c_{i}")
|
|
|
|
for i in range(2)
|
|
|
|
])
|
|
|
|
arrow_kw = {
|
|
|
|
"tip_config": {
|
|
|
|
"width": 0.2,
|
|
|
|
"length": 0.2,
|
|
|
|
},
|
|
|
|
"buff": 0.1,
|
|
|
|
"color": GREY_B,
|
|
|
|
}
|
|
|
|
# s_label_arrows = VGroup()
|
|
|
|
# for high_square, low_square, label in zip(*small_boards, s_labels):
|
|
|
|
# label.move_to(Group(high_square, low_square))
|
|
|
|
# label.arrows = VGroup(
|
|
|
|
# Arrow(label.get_bottom(), low_square.get_top(), **arrow_kw),
|
|
|
|
# Arrow(label.get_top(), high_square.get_bottom(), **arrow_kw),
|
|
|
|
# )
|
|
|
|
# s_label_arrows.add(*label.arrows)
|
|
|
|
|
|
|
|
# self.play(
|
|
|
|
# FadeIn(label),
|
|
|
|
# *map(GrowArrow, label.arrows)
|
|
|
|
# )
|
|
|
|
|
|
|
|
c_label_arrows = VGroup()
|
|
|
|
for label, coin in zip(c_labels, bin_coins):
|
|
|
|
label.next_to(coin, UP, LARGE_BUFF)
|
|
|
|
arrow = Arrow(label.get_bottom(), coin.get_top(), **arrow_kw)
|
|
|
|
c_label_arrows.add(arrow)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(label),
|
|
|
|
GrowArrow(arrow)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Coin 1 communicates location
|
|
|
|
bit1_rect = SurroundingRectangle(
|
|
|
|
VGroup(
|
|
|
|
bin_states[0][1],
|
|
|
|
bin_states[-1][1],
|
|
|
|
),
|
|
|
|
buff=SMALL_BUFF,
|
|
|
|
)
|
|
|
|
coin1_rect = SurroundingRectangle(
|
|
|
|
Group(c_labels[1], bin_coins[1]),
|
|
|
|
buff=SMALL_BUFF,
|
|
|
|
)
|
|
|
|
for rect in bit1_rect, coin1_rect:
|
|
|
|
rect.insert_n_curves(100)
|
|
|
|
nd = int(12 * get_norm(rect.get_area_vector()))
|
|
|
|
rect.become(DashedVMobject(rect, num_dashes=nd))
|
|
|
|
rect.set_stroke(WHITE, 2)
|
|
|
|
|
|
|
|
kw = {
|
|
|
|
"stroke_width": 2,
|
|
|
|
"stroke_color": YELLOW,
|
|
|
|
"buff": 0.05,
|
|
|
|
}
|
|
|
|
zero_rects, one_rects = [
|
|
|
|
VGroup(
|
|
|
|
SurroundingRectangle(bin_states[0][1], **kw),
|
|
|
|
SurroundingRectangle(bin_states[1][1], **kw),
|
|
|
|
),
|
|
|
|
VGroup(
|
|
|
|
SurroundingRectangle(bin_states[2][1], **kw),
|
|
|
|
SurroundingRectangle(bin_states[3][1], **kw),
|
|
|
|
),
|
|
|
|
]
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(bit1_rect),
|
|
|
|
ShowCreation(coin1_rect),
|
|
|
|
FadeOut(state_rect),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(board_rects[0].stretch, 0.5, 0, {"about_edge": LEFT})
|
|
|
|
self.play(ShowCreation(zero_rects))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FadeOut(board_rects[0]),
|
|
|
|
FadeOut(zero_rects),
|
|
|
|
FadeIn(board_rects[1])
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
board_rects[1].stretch, 0.5, 0, {"about_edge": RIGHT}
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(bin_coins[1]),
|
|
|
|
arrows[1].match_style, arrows[0],
|
|
|
|
arrows[3].match_style, arrows[1],
|
|
|
|
)
|
|
|
|
self.play(ShowCreation(one_rects[1]))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Talk about null bit
|
|
|
|
null_word = TextMobject("Null bit")
|
|
|
|
null_word.next_to(bin_coins[0], DOWN, buff=1.5, aligned_edge=LEFT)
|
|
|
|
null_arrow = Arrow(null_word.get_top(), bin_coins[0].get_bottom())
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
Write(null_word),
|
|
|
|
GrowArrow(null_arrow)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
for i in (0, 1, 0):
|
|
|
|
self.play(
|
|
|
|
FlipCoin(bin_coins[0]),
|
|
|
|
arrows[3 - i].match_style, arrows[0],
|
|
|
|
arrows[2 + i].match_style, arrows[3 - i],
|
|
|
|
FadeOut(one_rects[1 - i]),
|
|
|
|
FadeIn(one_rects[i]),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Written mathematically
|
|
|
|
frame.generate_target()
|
|
|
|
frame.target.set_height(10, about_edge=DOWN)
|
|
|
|
rule_words = TextMobject("Rule: Just look at coin 1")
|
|
|
|
rule_words.set_height(0.6)
|
|
|
|
rule_words.next_to(frame.target.get_corner(UL), DR, buff=0.5)
|
|
|
|
rule_arrow = Vector(1.5 * RIGHT)
|
|
|
|
rule_arrow.next_to(rule_words, RIGHT)
|
|
|
|
rule_arrow.set_color(BLUE)
|
|
|
|
rule_equation = TexMobject("S", "=", "c_1")
|
|
|
|
rule_equation_long = TexMobject("S", "=", "0", "\\cdot", "c_0", "+", "1", "\\cdot", "c_1")
|
|
|
|
for equation in rule_equation, rule_equation_long:
|
|
|
|
equation.set_color_by_tex("S", YELLOW)
|
|
|
|
equation.set_height(0.7)
|
|
|
|
equation.next_to(rule_arrow, RIGHT)
|
|
|
|
|
|
|
|
s_labels = VGroup(
|
|
|
|
TexMobject("S", "= 0"),
|
|
|
|
TexMobject("S", "= 1"),
|
|
|
|
)
|
|
|
|
for label, board in zip(s_labels, small_boards):
|
|
|
|
label.set_height(0.5)
|
|
|
|
label.next_to(board, UP)
|
|
|
|
label.set_color_by_tex("S", YELLOW)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(frame),
|
|
|
|
FadeIn(rule_words, 2 * DOWN)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
for label in s_labels:
|
|
|
|
self.play(Write(label))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
GrowArrow(rule_arrow),
|
|
|
|
FadeIn(rule_equation, LEFT),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
mid_equation = rule_equation_long[2:-1]
|
|
|
|
mid_equation.save_state()
|
|
|
|
mid_equation.scale(0, about_edge=LEFT)
|
|
|
|
self.play(
|
|
|
|
Transform(rule_equation[:2], rule_equation_long[:2]),
|
|
|
|
Transform(rule_equation[2], rule_equation_long[-1]),
|
|
|
|
Restore(mid_equation),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class WalkingTheSquare(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# Setup objects
|
|
|
|
plane = NumberPlane(
|
|
|
|
x_range=(-2, 2, 1),
|
|
|
|
y_range=(-2, 2, 1),
|
|
|
|
height=15,
|
|
|
|
width=15,
|
|
|
|
faded_line_ratio=3,
|
|
|
|
axis_config={"include_tip": False}
|
|
|
|
)
|
|
|
|
plane.move_to(1.5 * DOWN)
|
|
|
|
plane.add_coordinate_labels()
|
|
|
|
plane.x_axis.add_numbers([0])
|
|
|
|
|
|
|
|
board = Chessboard(shape=(1, 2))
|
|
|
|
board.set_height(1.5)
|
|
|
|
board.move_to(plane.c2p(-0.75, 0.75))
|
|
|
|
coins = CoinsOnBoard(
|
|
|
|
board,
|
|
|
|
coin_config={"numeric_labels": True}
|
|
|
|
)
|
|
|
|
coins.flip(RIGHT)
|
|
|
|
|
|
|
|
coords = [(0, 0), (0, 1), (1, 0), (1, 1)]
|
|
|
|
coord_labels = VGroup()
|
|
|
|
dots = VGroup()
|
|
|
|
for x, y in coords:
|
|
|
|
label = TexMobject(f"({x}, {y})")
|
|
|
|
point = plane.c2p(x, y)
|
|
|
|
label.next_to(point, UR, buff=0.25)
|
|
|
|
dot = Dot(point, radius=0.075)
|
|
|
|
dot.set_color(GREY)
|
|
|
|
dots.add(dot)
|
|
|
|
coord_labels.add(label)
|
|
|
|
|
|
|
|
active_dot = Dot(radius=0.15, color=YELLOW)
|
|
|
|
active_dot.move_to(plane.c2p(0, 0))
|
|
|
|
|
|
|
|
# Walk around square
|
|
|
|
self.play(Write(plane))
|
|
|
|
self.play(
|
|
|
|
FadeIn(board),
|
|
|
|
FadeIn(coins),
|
|
|
|
FadeIn(active_dot),
|
|
|
|
FadeIn(coord_labels[0]),
|
|
|
|
)
|
|
|
|
edges = VGroup()
|
|
|
|
for i, j, c in [(0, 1, 1), (1, 3, 0), (3, 2, 1), (2, 0, 0)]:
|
|
|
|
edge = Line(plane.c2p(*coords[i]), plane.c2p(*coords[j]))
|
|
|
|
edge.set_stroke(PINK, 3)
|
|
|
|
edges.add(edge)
|
|
|
|
|
|
|
|
anims = [
|
|
|
|
FlipCoin(coins[c]),
|
|
|
|
ShowCreation(edge),
|
|
|
|
ApplyMethod(active_dot.move_to, dots[j])
|
|
|
|
]
|
|
|
|
if j != 0:
|
|
|
|
anims += [
|
|
|
|
FadeInFromPoint(coord_labels[j], coord_labels[i].get_center()),
|
|
|
|
]
|
|
|
|
self.add(edge, dots[i], active_dot)
|
|
|
|
self.play(*anims)
|
|
|
|
self.add(edges, dots, active_dot)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show a few more flips
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
active_dot.move_to, dots[2],
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
active_dot.move_to, dots[3],
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
active_dot.move_to, dots[2],
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Circles illustrating scheme
|
|
|
|
low_rect = SurroundingRectangle(
|
|
|
|
VGroup(edges[3], coord_labels[0], coord_labels[2], plane.x_axis.numbers[-1]),
|
|
|
|
buff=0.25,
|
|
|
|
)
|
|
|
|
low_rect.round_corners()
|
|
|
|
low_rect.insert_n_curves(30)
|
|
|
|
low_rect.set_stroke(YELLOW, 3)
|
|
|
|
high_rect = low_rect.copy()
|
|
|
|
high_rect.shift(dots[1].get_center() - dots[0].get_center())
|
|
|
|
|
|
|
|
key = Key()
|
|
|
|
key.set_color(YELLOW)
|
|
|
|
key.set_gloss(0)
|
|
|
|
key.match_width(board[0])
|
|
|
|
key.next_to(board[0], UP, SMALL_BUFF)
|
|
|
|
|
|
|
|
s_labels = VGroup(
|
|
|
|
TexMobject("S = 0").next_to(low_rect, UP),
|
|
|
|
TexMobject("S = 1").next_to(high_rect, UP),
|
|
|
|
)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
ShowCreation(low_rect),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FadeIn(key, DOWN),
|
|
|
|
FadeIn(s_labels[0], DOWN),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
active_dot.move_to, dots[0],
|
|
|
|
)
|
|
|
|
self.wait(0.5)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
active_dot.move_to, dots[2],
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(low_rect, high_rect),
|
|
|
|
FadeIn(s_labels[1], DOWN),
|
|
|
|
low_rect.set_stroke, GREY, 1,
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
active_dot.move_to, dots[3],
|
|
|
|
key.match_x, board[1],
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[0]),
|
|
|
|
active_dot.move_to, dots[1],
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
active_dot.move_to, dots[0],
|
|
|
|
key.match_x, board[0],
|
|
|
|
high_rect.match_style, low_rect,
|
|
|
|
low_rect.match_style, high_rect,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class ThreeSquareCase(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# Show sequence of boards
|
|
|
|
boards = Group(
|
|
|
|
Chessboard(shape=(1, 2), height=0.25 * 1),
|
|
|
|
Chessboard(shape=(1, 3), height=0.25 * 1),
|
|
|
|
Chessboard(shape=(2, 2), height=0.25 * 2),
|
|
|
|
Chessboard(shape=(8, 8), height=0.25 * 8),
|
|
|
|
)
|
|
|
|
dots = TexMobject("\\dots")
|
|
|
|
group = Group(*boards[:3], dots, boards[3])
|
|
|
|
|
|
|
|
group.arrange(RIGHT)
|
|
|
|
group.set_width(FRAME_WIDTH - 1)
|
|
|
|
|
|
|
|
board_groups = Group()
|
|
|
|
for board in boards:
|
|
|
|
board.coins = CoinsOnBoard(board, coin_config={"numeric_labels": True})
|
|
|
|
board_groups.add(Group(board, board.coins))
|
|
|
|
boards[0].coins.flip_at_random()
|
|
|
|
boards[1].coins.flip_by_bools([False, True, False])
|
|
|
|
boards[2].coins.flip_at_random()
|
|
|
|
boards[3].coins.flip_by_message("3 Fails!")
|
|
|
|
|
|
|
|
def get_board_transform(i, bgs=board_groups):
|
|
|
|
return TransformFromCopy(
|
|
|
|
bgs[i], bgs[i + 1],
|
|
|
|
path_arc=PI / 2,
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
|
|
|
|
self.add(board_groups[0])
|
|
|
|
self.play(get_board_transform(0))
|
|
|
|
self.play(get_board_transform(1))
|
|
|
|
self.play(get_board_transform(2), Write(dots))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Isolate 3 square board
|
|
|
|
board_group = board_groups[1]
|
|
|
|
board = boards[1]
|
|
|
|
coins = board.coins
|
|
|
|
|
|
|
|
title = TextMobject("Three square case")
|
|
|
|
title.set_height(0.7)
|
|
|
|
title.to_edge(UP)
|
|
|
|
|
|
|
|
board_group.generate_target()
|
|
|
|
board_group.target.set_width(4)
|
|
|
|
board_group.target.move_to(DOWN, OUT)
|
|
|
|
|
|
|
|
self.save_state()
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(board_group, rate_func=squish_rate_func(smooth, 0.5, 1)),
|
|
|
|
Write(title),
|
|
|
|
LaggedStartMap(FadeOut, Group(
|
|
|
|
board_groups[0],
|
|
|
|
board_groups[2],
|
|
|
|
dots,
|
|
|
|
board_groups[3],
|
|
|
|
), lambda m: (m, DOWN)),
|
|
|
|
run_time=2,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Try 0*c0 + 1*c1 + 2*c2
|
|
|
|
s_sum = TexMobject(
|
|
|
|
"0", "\\cdot", "c_0", "+",
|
|
|
|
"1", "\\cdot", "c_1", "+",
|
|
|
|
"2", "\\cdot", "c_2",
|
|
|
|
)
|
|
|
|
s_sum.set_height(0.6)
|
|
|
|
c_sum = s_sum.copy()
|
|
|
|
s_sum.center().to_edge(UP)
|
|
|
|
c_sum.next_to(s_sum, DOWN, LARGE_BUFF)
|
|
|
|
|
|
|
|
coin_copies = Group()
|
|
|
|
for i in range(3):
|
|
|
|
part = c_sum.get_part_by_tex(f"c_{i}")
|
|
|
|
coin_copy = coins[i].copy()
|
|
|
|
coin_copy.set_height(1.2 * c_sum[0].get_height())
|
|
|
|
coin_copy.move_to(part)
|
|
|
|
coin_copy.align_to(c_sum, UP)
|
|
|
|
part.set_opacity(0)
|
|
|
|
coin_copies.add(coin_copy)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeOut(title),
|
|
|
|
FadeIn(s_sum[:3]),
|
|
|
|
FadeIn(c_sum[:2]),
|
|
|
|
)
|
|
|
|
self.play(TransformFromCopy(coins[0], coin_copies[0]))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FadeIn(s_sum[3:7]),
|
|
|
|
FadeIn(c_sum[3:6]),
|
|
|
|
)
|
|
|
|
self.play(TransformFromCopy(coins[1], coin_copies[1]))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FadeIn(s_sum[7:11]),
|
|
|
|
FadeIn(c_sum[7:10]),
|
|
|
|
)
|
|
|
|
self.play(TransformFromCopy(coins[2], coin_copies[2]))
|
|
|
|
self.wait()
|
|
|
|
self.add(s_sum, c_sum, coin_copies)
|
|
|
|
|
|
|
|
rhs = VGroup(TexMobject("="), Integer(1))
|
|
|
|
rhs.arrange(RIGHT)
|
|
|
|
rhs[1].set_color(YELLOW)
|
|
|
|
rhs.match_height(c_sum[0])
|
|
|
|
rhs.next_to(c_sum, RIGHT, aligned_edge=UP)
|
|
|
|
braces = VGroup(
|
|
|
|
Brace(c_sum[0:3], DOWN),
|
|
|
|
Brace(c_sum[0:7], DOWN),
|
|
|
|
Brace(c_sum[0:11], DOWN),
|
|
|
|
)
|
|
|
|
for brace, n in zip(braces, [0, 1, 1]):
|
|
|
|
brace.add(brace.get_tex(n))
|
|
|
|
brace.unlock_triangulation()
|
|
|
|
self.play(GrowFromCenter(braces[0]))
|
|
|
|
self.wait()
|
|
|
|
self.play(ReplacementTransform(braces[0], braces[1]))
|
|
|
|
self.wait()
|
|
|
|
self.play(ReplacementTransform(braces[1], braces[2]))
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(braces[2][-1], rhs[1], path_arc=-PI / 2),
|
|
|
|
Write(rhs[0]),
|
|
|
|
)
|
|
|
|
self.play(FadeOut(braces[2]))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show values of S
|
|
|
|
s_labels = VGroup(*[
|
|
|
|
TexMobject(f"S = {n}")
|
|
|
|
for n in range(3)
|
|
|
|
])
|
|
|
|
for label, square in zip(s_labels, board):
|
|
|
|
label.next_to(square, UP)
|
|
|
|
label.set_width(0.8 * square.get_width())
|
|
|
|
label.set_color(YELLOW)
|
|
|
|
|
|
|
|
key = Key(depth_test=False)
|
|
|
|
key.set_stroke(BLACK, 3, background=True)
|
|
|
|
key.set_width(0.8 * board[0].get_width())
|
|
|
|
key.move_to(board[0])
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
coins.next_to, board, DOWN,
|
|
|
|
coins.match_z, coins,
|
|
|
|
board.set_opacity, 0.75,
|
|
|
|
FadeIn(key),
|
|
|
|
FadeIn(s_labels[0])
|
|
|
|
)
|
|
|
|
self.wait(0.5)
|
|
|
|
for i in (1, 2):
|
|
|
|
self.play(
|
|
|
|
ApplyMethod(key.move_to, board[i], path_arc=-45 * DEGREES),
|
|
|
|
s_labels[i - 1].set_fill, GREY, 0.25,
|
|
|
|
FadeIn(s_labels[i]),
|
|
|
|
)
|
|
|
|
self.wait(0.5)
|
|
|
|
|
|
|
|
# Mod 3 label
|
|
|
|
mod3_label = TextMobject("(mod 3)")
|
|
|
|
mod3_label.match_height(s_sum)
|
|
|
|
mod3_label.set_color(BLUE)
|
|
|
|
mod3_label.next_to(s_sum, RIGHT, buff=0.75)
|
|
|
|
|
|
|
|
rhs_rhs = TexMobject("\\equiv 0")
|
|
|
|
rhs_rhs.match_height(rhs)
|
|
|
|
rhs_rhs.next_to(rhs, RIGHT)
|
|
|
|
|
|
|
|
self.play(Write(mod3_label))
|
|
|
|
self.wait()
|
|
|
|
rhs[1].unlock_triangulation()
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[2]),
|
|
|
|
FlipCoin(coin_copies[2]),
|
|
|
|
rhs[1].set_value, 3,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(Write(rhs_rhs))
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
rhs[1].set_value, 0,
|
|
|
|
FadeOut(rhs_rhs)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Show a few flips
|
|
|
|
for i in [2, 1, 0, 2, 1, 2, 0]:
|
|
|
|
bools = coins.get_bools()
|
|
|
|
bools[i] = not bools[i]
|
|
|
|
new_sum = sum([n * b for n, b in enumerate(bools)]) % 3
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[i]),
|
|
|
|
FlipCoin(coin_copies[i]),
|
|
|
|
rhs[1].set_value, new_sum,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Show general sum
|
|
|
|
general_sum = TexMobject(r"\sum ^{63}_{n=0}n\cdot c_n")
|
|
|
|
mod_64 = TextMobject("(mod 64)")
|
|
|
|
mod_64.next_to(general_sum, DOWN)
|
|
|
|
general_sum.add(mod_64)
|
|
|
|
general_sum.to_corner(UL)
|
|
|
|
|
|
|
|
self.play(FadeIn(general_sum))
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeOut(general_sum))
|
|
|
|
|
|
|
|
# Walk through 010 example
|
|
|
|
self.play(
|
|
|
|
s_labels[2].set_fill, GREY, 0.25,
|
|
|
|
s_labels[0].set_fill, YELLOW, 1,
|
|
|
|
ApplyMethod(key.move_to, board[0], path_arc=30 * DEGREES)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
FlipCoin(coin_copies[1]),
|
|
|
|
rhs[1].set_value, 0,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
square = Square()
|
|
|
|
square.set_stroke(YELLOW, 3)
|
|
|
|
square.replace(board[0])
|
|
|
|
square[0].move_to(board[0], OUT)
|
|
|
|
self.play(ShowCreation(square))
|
|
|
|
self.wait()
|
|
|
|
self.play(FadeOut(square))
|
|
|
|
|
|
|
|
# Walk through alternate flip on 010 example
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
FlipCoin(coin_copies[1]),
|
|
|
|
rhs[1].set_value, 1,
|
|
|
|
)
|
|
|
|
|
|
|
|
morty = Mortimer(height=1.5, mode="hooray")
|
|
|
|
morty.to_corner(DR)
|
|
|
|
bubble = SpeechBubble(height=2, width=2)
|
|
|
|
bubble.pin_to(morty)
|
|
|
|
bubble.write("There's another\\\\way!")
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(morty),
|
|
|
|
ShowCreation(bubble),
|
|
|
|
Write(bubble.content, run_time=1),
|
|
|
|
)
|
|
|
|
self.play(Blink(morty))
|
|
|
|
self.play(
|
|
|
|
FadeOut(VGroup(morty, bubble, bubble.content))
|
|
|
|
)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[2]),
|
|
|
|
FlipCoin(coin_copies[2]),
|
|
|
|
rhs[1].set_value, 3,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.play(rhs[1].set_value, 0)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class TreeOfThreeFlips(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# Setup sums
|
|
|
|
csum = Group(
|
|
|
|
TexMobject("0 \\cdot"),
|
|
|
|
Coin(numeric_labels=True),
|
|
|
|
TexMobject("+\\,1 \\cdot"),
|
|
|
|
Coin(numeric_labels=True),
|
|
|
|
TexMobject("+\\,2 \\cdot"),
|
|
|
|
Coin(numeric_labels=True),
|
|
|
|
TexMobject("="),
|
|
|
|
Integer(0)
|
|
|
|
)
|
|
|
|
csum.coins = csum[1:7:2]
|
|
|
|
csum.coins.set_height(1.5 * csum[0].get_height())
|
|
|
|
csum.coins.flip(RIGHT)
|
|
|
|
csum.coins[1].flip(RIGHT)
|
|
|
|
csum.arrange(RIGHT, buff=0.1)
|
|
|
|
csum[-1].align_to(csum[0], DOWN)
|
|
|
|
csum[-1].shift(SMALL_BUFF * RIGHT)
|
|
|
|
csum.to_edge(LEFT)
|
|
|
|
|
|
|
|
csum_rect = SurroundingRectangle(csum, buff=SMALL_BUFF)
|
|
|
|
csum_rect.set_stroke(WHITE, 1)
|
|
|
|
|
|
|
|
# Set rhs values
|
|
|
|
def set_rhs_target(cs, colors=[RED, GREEN, BLUE]):
|
|
|
|
bools = [c.is_heads() for c in cs.coins]
|
|
|
|
value = sum([n * b for n, b in enumerate(bools)]) % 3
|
|
|
|
cs[-1].unlock_triangulation()
|
|
|
|
cs[-1].generate_target()
|
|
|
|
cs[-1].target.set_value(value)
|
|
|
|
cs[-1].target.set_color(colors[value])
|
|
|
|
return cs[-1]
|
|
|
|
|
|
|
|
rhs = set_rhs_target(csum)
|
|
|
|
rhs.become(rhs.target)
|
|
|
|
|
|
|
|
# Create copies
|
|
|
|
new_csums = Group()
|
|
|
|
for i in range(3):
|
|
|
|
new_csum = csum.deepcopy()
|
|
|
|
new_csum.coins = new_csum[1:7:2]
|
|
|
|
new_csums.add(new_csum)
|
|
|
|
new_csums.arrange(DOWN, buff=1.5)
|
|
|
|
new_csums.next_to(csum, RIGHT, buff=3)
|
|
|
|
|
|
|
|
# Arrows
|
|
|
|
arrows = VGroup()
|
|
|
|
for i, ncs in enumerate(new_csums):
|
|
|
|
arrow = Arrow(csum_rect.get_right(), ncs.get_left())
|
|
|
|
label = TextMobject(f"Flip coin {i}")
|
|
|
|
label.set_height(0.3)
|
|
|
|
label.set_fill(GREY_A)
|
|
|
|
label.set_stroke(BLACK, 3, background=True)
|
|
|
|
label.next_to(ORIGIN, UP, buff=0)
|
|
|
|
label.rotate(arrow.get_angle(), about_point=ORIGIN)
|
|
|
|
label.shift(arrow.get_center())
|
|
|
|
arrow.label = label
|
|
|
|
arrows.add(arrow)
|
|
|
|
arrows.set_color(GREY)
|
|
|
|
|
|
|
|
# Initial state label
|
|
|
|
is_label = TextMobject(
|
|
|
|
"Initial state: 010",
|
|
|
|
tex_to_color_map={"0": RED_D, "1": GOLD_D}
|
|
|
|
)
|
|
|
|
is_label.set_height(0.4)
|
|
|
|
is_label.next_to(csum_rect, UP, aligned_edge=LEFT)
|
|
|
|
|
|
|
|
# Show three flips
|
|
|
|
self.add(csum)
|
|
|
|
self.add(csum_rect)
|
|
|
|
self.add(is_label)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
anims = []
|
|
|
|
for i, arrow, ncs in zip(it.count(), arrows, new_csums):
|
|
|
|
anims += [
|
|
|
|
GrowArrow(arrow),
|
|
|
|
FadeIn(arrow.label, lag_ratio=0.2),
|
|
|
|
]
|
|
|
|
self.play(LaggedStart(*anims))
|
|
|
|
for i, ncs in enumerate(new_csums):
|
|
|
|
ncs.coins[i].flip()
|
|
|
|
rhs = set_rhs_target(ncs)
|
|
|
|
ncs.coins[i].flip()
|
|
|
|
self.play(
|
|
|
|
TransformFromCopy(csum, ncs, path_arc=30 * DEGREES)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(ncs.coins[i]),
|
|
|
|
MoveToTarget(rhs)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Put key in square 2
|
|
|
|
board = Chessboard(shape=(1, 3), square_resolution=(5, 5))
|
|
|
|
board.set_gloss(0.5)
|
|
|
|
board.set_width(3)
|
|
|
|
board.set_depth(0.25, stretch=True)
|
|
|
|
board.space_out_submobjects(factor=1.001)
|
|
|
|
board.next_to(ORIGIN, LEFT)
|
|
|
|
board.to_edge(UP)
|
|
|
|
board.shift(IN)
|
|
|
|
board.rotate(60 * DEGREES, LEFT)
|
|
|
|
opening_square = board[2][0]
|
|
|
|
opening_square_top = opening_square.get_corner(UP + IN)
|
|
|
|
|
|
|
|
key = Key()
|
|
|
|
key.to_corner(UL, buff=LARGE_BUFF)
|
|
|
|
key.shift(OUT)
|
|
|
|
key.generate_target()
|
|
|
|
key.target.scale(0.3)
|
|
|
|
key.target.rotate(60 * DEGREES, LEFT)
|
|
|
|
key.target.move_to(board[2][0])
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(board, DOWN),
|
|
|
|
FadeIn(key)
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(key, path_arc=30 * DEGREES),
|
|
|
|
Rotate(opening_square, 90 * DEGREES, LEFT, about_point=opening_square_top),
|
|
|
|
)
|
|
|
|
self.play(
|
|
|
|
Rotate(opening_square, 90 * DEGREES, RIGHT, about_point=opening_square_top),
|
|
|
|
key.next_to, board[1], RIGHT, buff=0.01,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
self.remove(key)
|
|
|
|
self.play(Rotate(board, 0 * DEGREES, RIGHT, run_time=0))
|
|
|
|
self.play(Rotate(board, 60 * DEGREES, RIGHT))
|
|
|
|
|
|
|
|
# Put coins on
|
|
|
|
coins = csum.coins.copy()
|
|
|
|
for coin, cube in zip(coins, board):
|
|
|
|
coin.generate_target()
|
|
|
|
coin.target.next_to(cube, OUT, buff=0)
|
|
|
|
self.play(LaggedStartMap(MoveToTarget, coins, run_time=2))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
|
|
|
|
class ShowCube(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# ...
|
|
|
|
frame = self.camera.frame
|
|
|
|
light = self.camera.light_source
|
|
|
|
light.move_to([-10, -10, 20])
|
|
|
|
|
|
|
|
# Plane and axes
|
|
|
|
plane = NumberPlane(
|
|
|
|
x_range=(-2, 2, 1),
|
|
|
|
y_range=(-2, 2, 1),
|
|
|
|
height=15,
|
|
|
|
width=15,
|
|
|
|
faded_line_ratio=3,
|
|
|
|
axis_config={"include_tip": False}
|
|
|
|
)
|
|
|
|
plane.add_coordinate_labels()
|
|
|
|
plane.coordinate_labels.set_stroke(width=0)
|
|
|
|
axes = ThreeDAxes(
|
|
|
|
x_range=(-2, 2, 1),
|
|
|
|
y_range=(-2, 2, 1),
|
|
|
|
z_range=(-2, 2, 1),
|
|
|
|
height=15,
|
|
|
|
width=15,
|
|
|
|
depth=15,
|
|
|
|
)
|
|
|
|
|
|
|
|
# Vertices and edges
|
|
|
|
vert_coords = [
|
|
|
|
(n % 2, (n // 2) % 2, (n // 4) % 2)
|
|
|
|
for n in range(8)
|
|
|
|
]
|
|
|
|
verts = []
|
|
|
|
coord_labels = VGroup()
|
|
|
|
coord_labels_2d = VGroup()
|
|
|
|
for coords in vert_coords:
|
|
|
|
vert = axes.c2p(*coords)
|
|
|
|
verts.append(vert)
|
|
|
|
x, y, z = coords
|
|
|
|
label = TexMobject(f"({x}, {y}, {z})")
|
|
|
|
label.set_height(0.3)
|
|
|
|
label.next_to(vert, UR, SMALL_BUFF)
|
|
|
|
label.rotate(89 * DEGREES, RIGHT, about_point=vert)
|
|
|
|
coord_labels.add(label)
|
|
|
|
if z == 0:
|
|
|
|
label_2d = TexMobject(f"({x}, {y})")
|
|
|
|
label_2d.set_height(0.3)
|
|
|
|
label_2d.next_to(vert, UR, SMALL_BUFF)
|
|
|
|
coord_labels_2d.add(label_2d)
|
|
|
|
coord_labels.set_stroke(BLACK, 3, background=True)
|
|
|
|
|
|
|
|
edge_indices = [
|
|
|
|
(0, 1), (0, 2), (1, 3), (2, 3),
|
|
|
|
(0, 4), (1, 5), (2, 6), (3, 7),
|
|
|
|
(4, 5), (4, 6), (5, 7), (6, 7),
|
|
|
|
]
|
|
|
|
|
|
|
|
# Vertex and edge drawings
|
|
|
|
spheres = SGroup()
|
|
|
|
for vert in verts:
|
|
|
|
sphere = Sphere(
|
|
|
|
radius=0.1,
|
|
|
|
resolution=(51, 26),
|
|
|
|
)
|
|
|
|
sphere.set_gloss(0.3)
|
|
|
|
sphere.set_color(GREY)
|
|
|
|
sphere.move_to(vert)
|
|
|
|
spheres.add(sphere)
|
|
|
|
|
|
|
|
edges = SGroup()
|
|
|
|
for i, j in edge_indices:
|
|
|
|
edge = Line3D(verts[i], verts[j])
|
|
|
|
edge.set_color(TEAL)
|
|
|
|
edges.add(edge)
|
|
|
|
|
|
|
|
# Setup highlight animations
|
|
|
|
def highlight(n, spheres=spheres, coord_labels=coord_labels):
|
|
|
|
anims = []
|
|
|
|
for k, sphere, cl in zip(it.count(), spheres, coord_labels):
|
|
|
|
if k == n:
|
|
|
|
sphere.save_state()
|
|
|
|
cl.save_state()
|
|
|
|
sphere.generate_target()
|
|
|
|
cl.generate_target()
|
|
|
|
cl.target.set_fill(YELLOW)
|
|
|
|
sphere.target.set_color(YELLOW)
|
|
|
|
Group(cl.target, sphere.target).scale(1.5, about_point=sphere.get_center())
|
|
|
|
anims += [
|
|
|
|
MoveToTarget(sphere),
|
|
|
|
MoveToTarget(cl),
|
|
|
|
]
|
|
|
|
elif sphere.get_color() == Color(YELLOW):
|
|
|
|
anims += [
|
|
|
|
Restore(sphere),
|
|
|
|
Restore(cl),
|
|
|
|
]
|
|
|
|
return AnimationGroup(*anims)
|
|
|
|
|
|
|
|
# Setup 2d case
|
|
|
|
frame.move_to(1.5 * UP)
|
|
|
|
self.add(plane)
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(FadeIn, coord_labels_2d),
|
|
|
|
LaggedStartMap(GrowFromCenter, spheres[:4]),
|
|
|
|
LaggedStartMap(GrowFromCenter, edges[:4]),
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Transition to 3d case
|
|
|
|
frame.generate_target()
|
|
|
|
frame.target.set_rotation(-25 * DEGREES, 70 * DEGREES)
|
|
|
|
frame.target.move_to([1, 2, 0])
|
|
|
|
frame.target.set_height(10)
|
|
|
|
to_grow = Group(*edges[4:], *spheres[4:], *coord_labels[4:])
|
|
|
|
to_grow.save_state()
|
|
|
|
to_grow.set_depth(0, about_edge=IN, stretch=True)
|
|
|
|
|
|
|
|
rf = squish_rate_func(smooth, 0.5, 1)
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(frame),
|
|
|
|
ShowCreation(axes.z_axis),
|
|
|
|
Restore(to_grow, rate_func=rf),
|
|
|
|
FadeOut(coord_labels_2d, rate_func=rf),
|
|
|
|
*[
|
|
|
|
FadeInFromPoint(cl, cl2.get_center(), rate_func=squish_rate_func(smooth, 0.5, 1))
|
|
|
|
for cl, cl2 in zip(coord_labels[:4], coord_labels_2d)
|
|
|
|
],
|
|
|
|
run_time=3
|
|
|
|
)
|
|
|
|
|
|
|
|
frame.start_time = self.time
|
|
|
|
frame.scene = self
|
|
|
|
frame.add_updater(lambda m: m.set_theta(
|
|
|
|
-25 * DEGREES * math.cos((m.scene.time - m.start_time) * PI / 60)
|
|
|
|
))
|
|
|
|
|
|
|
|
self.add(axes.z_axis)
|
|
|
|
self.add(edges)
|
|
|
|
self.add(spheres)
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
LaggedStart(*[Indicate(s, color=GREEN) for s in spheres], run_time=2, lag_ratio=0.1),
|
|
|
|
LaggedStart(*[Indicate(c, color=GREEN) for c in coord_labels], run_time=2, lag_ratio=0.1),
|
|
|
|
)
|
|
|
|
|
|
|
|
# Add chessboard
|
|
|
|
board = Chessboard(
|
|
|
|
shape=(1, 3), height=1,
|
|
|
|
square_resolution=(5, 5),
|
|
|
|
)
|
|
|
|
board.move_to(plane.c2p(-1, 0), DOWN + IN)
|
|
|
|
coins = CoinsOnBoard(board, coin_config={"numeric_labels": True})
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
FadeIn(board),
|
|
|
|
FadeIn(coins),
|
|
|
|
highlight(7)
|
|
|
|
)
|
|
|
|
|
|
|
|
# Walk along a few edges
|
|
|
|
for ci in [1, 2, 0, 1, 2, 1, 0, 1]:
|
|
|
|
coin = coins[ci]
|
|
|
|
curr_n = sum([(2**k) * c.is_heads() for k, c in enumerate(coins)])
|
|
|
|
coin.flip()
|
|
|
|
new_n = sum([(2**k) * c.is_heads() for k, c in enumerate(coins)])
|
|
|
|
coin.flip()
|
|
|
|
line = Line(verts[curr_n], verts[new_n])
|
|
|
|
line.set_stroke(YELLOW, 3)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coin),
|
|
|
|
highlight(new_n),
|
|
|
|
ShowCreationThenDestruction(line)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
# Color the corners
|
|
|
|
self.play(highlight(-1))
|
|
|
|
|
|
|
|
colors = [RED, GREEN, BLUE]
|
|
|
|
anims = []
|
|
|
|
for sphere, cl, coords in zip(spheres, coord_labels, vert_coords):
|
|
|
|
color = colors[sum([n * x for n, x in enumerate(coords)]) % 3]
|
|
|
|
sphere.generate_target()
|
|
|
|
cl.generate_target()
|
|
|
|
sphere.target.set_color(color)
|
|
|
|
cl.target.set_fill(color)
|
|
|
|
anims += [MoveToTarget(sphere), MoveToTarget(cl)]
|
|
|
|
|
|
|
|
self.play(
|
|
|
|
edges.set_color, GREY, 0.5,
|
|
|
|
LaggedStart(*anims, run_time=3)
|
|
|
|
)
|
|
|
|
self.wait(5)
|
|
|
|
|
|
|
|
# Focus on (0, 0, 0)
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(FlipCoin, coins),
|
|
|
|
coord_labels[1:].set_opacity, 0.2,
|
|
|
|
spheres[1:].set_opacity, 0.2,
|
|
|
|
)
|
|
|
|
self.wait(2)
|
|
|
|
|
|
|
|
lines = VGroup()
|
|
|
|
for n in [1, 2, 4]:
|
|
|
|
line = Line(verts[0], verts[n], buff=0.1)
|
|
|
|
line.set_stroke(YELLOW, 3)
|
|
|
|
coin = coins[int(np.log2(n))]
|
|
|
|
self.play(
|
|
|
|
ShowCreationThenDestruction(line),
|
|
|
|
spheres[n].set_opacity, 1,
|
|
|
|
coord_labels[n].set_opacity, 1,
|
|
|
|
spheres[0].set_opacity, 0.2,
|
|
|
|
coord_labels[0].set_opacity, 0.2,
|
|
|
|
FlipCoin(coin)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
line.reverse_points()
|
|
|
|
self.add(line, coord_labels)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coin),
|
|
|
|
ShowCreation(line)
|
|
|
|
)
|
|
|
|
lines.add(line)
|
|
|
|
self.wait(15)
|
|
|
|
|
|
|
|
# Focus on (0, 1, 0)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coins[1]),
|
|
|
|
Uncreate(lines[1]),
|
|
|
|
FadeOut(lines[::2]),
|
|
|
|
Group(
|
|
|
|
spheres[1], coord_labels[1],
|
|
|
|
spheres[4], coord_labels[4],
|
|
|
|
).set_opacity, 0.2,
|
|
|
|
)
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
lines = VGroup()
|
|
|
|
curr_n = 2
|
|
|
|
for n in [1, 2, 4]:
|
|
|
|
new_n = n ^ curr_n
|
|
|
|
line = Line(verts[curr_n], verts[new_n], buff=0.1)
|
|
|
|
line.set_stroke(YELLOW, 3)
|
|
|
|
coin = coins[int(np.log2(n))]
|
|
|
|
self.play(
|
|
|
|
ShowCreationThenDestruction(line),
|
|
|
|
spheres[new_n].set_opacity, 1,
|
|
|
|
coord_labels[new_n].set_opacity, 1,
|
|
|
|
# spheres[curr_n].set_opacity, 0.2,
|
|
|
|
# coord_labels[curr_n].set_opacity, 0.2,
|
|
|
|
FlipCoin(coin)
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
line.reverse_points()
|
|
|
|
self.add(line, coord_labels)
|
|
|
|
self.play(
|
|
|
|
FlipCoin(coin),
|
|
|
|
ShowCreation(line)
|
|
|
|
)
|
|
|
|
lines.add(line)
|
|
|
|
self.wait(10)
|
|
|
|
|
|
|
|
# Show a few more colorings
|
|
|
|
self.play(
|
|
|
|
LaggedStartMap(Uncreate, lines),
|
|
|
|
spheres.set_opacity, 1,
|
|
|
|
coord_labels.set_opacity, 1,
|
|
|
|
)
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
title = TextMobject("Strategy", "\\, $\\Leftrightarrow$ \\,", "Coloring")
|
|
|
|
title[2].set_submobject_colors_by_gradient(*colors)
|
|
|
|
title.set_stroke(BLACK, 5, background=True)
|
|
|
|
title.set_height(0.7)
|
|
|
|
title.to_edge(UP)
|
|
|
|
title.fix_in_frame()
|
|
|
|
|
|
|
|
def get_coloring_animation(ns, spheres=spheres, coord_labels=coord_labels, color=colors):
|
|
|
|
anims = []
|
|
|
|
for n, sphere, cl in zip(ns, spheres, coord_labels):
|
|
|
|
color = colors[int(n)]
|
|
|
|
sphere.generate_target()
|
|
|
|
cl.generate_target()
|
|
|
|
sphere.target.set_color(color)
|
|
|
|
cl.target.set_fill(color)
|
|
|
|
anims += [MoveToTarget(sphere), MoveToTarget(cl)]
|
|
|
|
return LaggedStart(*anims, run_time=1)
|
|
|
|
|
|
|
|
self.play(FadeIn(title, DOWN))
|
|
|
|
for x in range(5):
|
|
|
|
self.play(get_coloring_animation(np.random.randint(0, 3, 8)))
|
|
|
|
self.wait()
|
|
|
|
|
|
|
|
S0 = TexMobject("S = 0")
|
|
|
|
S0.to_edge(LEFT)
|
|
|
|
S0.shift(UP)
|
|
|
|
S0.fix_in_frame()
|
|
|
|
self.play(
|
|
|
|
FadeIn(S0, DOWN),
|
|
|
|
get_coloring_animation([0] * 8)
|
|
|
|
)
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
bit_sum = TexMobject("S = &(c_0 + c_1 + c_2) \\\\ &\\quad \\mod 3")
|
|
|
|
bit_sum.to_edge(LEFT)
|
|
|
|
bit_sum.shift(UP)
|
|
|
|
bit_sum.fix_in_frame()
|
|
|
|
self.play(
|
|
|
|
FadeIn(bit_sum, DOWN),
|
|
|
|
FadeOut(S0, UP),
|
|
|
|
get_coloring_animation([sum(coords) % 3 for coords in vert_coords])
|
|
|
|
)
|
|
|
|
self.wait(3)
|
|
|
|
self.play(FadeOut(bit_sum, UP))
|
|
|
|
|
|
|
|
# Count all strategies
|
|
|
|
count = TextMobject("$3^8$ total strategies")
|
|
|
|
count64 = TextMobject("$64^{(2^{64})}$ total strategies")
|
|
|
|
for words in count, count64:
|
|
|
|
words.to_edge(LEFT, buff=MED_SMALL_BUFF)
|
|
|
|
words.shift(UP)
|
|
|
|
words.fix_in_frame()
|
|
|
|
|
|
|
|
full_board = Chessboard()
|
|
|
|
full_board.set_height(6)
|
|
|
|
full_board.next_to(axes.c2p(0, 0, 0), np.array([-1, 1, 1]), buff=0)
|
|
|
|
full_board.shift(SMALL_BUFF * UP + LEFT)
|
|
|
|
|
|
|
|
full_coins = CoinsOnBoard(full_board, coin_config={"numeric_labels": True})
|
|
|
|
full_coins.flip_by_message("64^ 2^64")
|
|
|
|
|
|
|
|
self.play(FadeIn(count, DOWN))
|
|
|
|
self.wait(10)
|
|
|
|
self.remove(board, coins)
|
|
|
|
frame.clear_updaters()
|
|
|
|
frame.generate_target()
|
|
|
|
frame.target.set_rotation(0, 45 * DEGREES)
|
|
|
|
frame.target.shift(2 * UP)
|
|
|
|
self.play(
|
|
|
|
count.shift, UP,
|
|
|
|
count.set_opacity, 0.5,
|
|
|
|
ShowIncreasingSubsets(full_board, run_time=3),
|
|
|
|
ShowIncreasingSubsets(full_coins, run_time=3),
|
|
|
|
FadeIn(count64, DOWN),
|
|
|
|
MoveToTarget(frame, run_time=3)
|
|
|
|
)
|
|
|
|
self.wait(3)
|
|
|
|
|
|
|
|
frame.generate_target()
|
|
|
|
frame.target.shift(2 * DOWN)
|
|
|
|
frame.target.set_rotation(-15 * DEGREES, 70 * DEGREES)
|
|
|
|
self.play(
|
|
|
|
MoveToTarget(frame, run_time=3),
|
|
|
|
LaggedStartMap(FadeOut, full_board),
|
|
|
|
LaggedStartMap(FadeOut, full_coins),
|
|
|
|
FadeOut(count),
|
|
|
|
FadeOut(count64),
|
|
|
|
)
|
|
|
|
frame.add_updater(lambda m, dt: m.increment_theta(0.01 * dt))
|
|
|
|
self.wait(15)
|
|
|
|
|
|
|
|
|
|
|
|
class CubeSupplement(ThreeDScene):
|
|
|
|
def construct(self):
|
|
|
|
# Map 8 states to square choices
|
|
|
|
boards = Group(*[Chessboard(shape=(1, 3)) for x in range(8)])
|
|
|
|
boards.arrange(DOWN, buff=MED_LARGE_BUFF)
|
|
|
|
boards.set_height(7)
|
|
|
|
boards.to_edge(LEFT)
|
|
|
|
|
|
|
|
self.add(boards)
|
|
|
|
|
|
|
|
# Associate choices with colors
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
self.embed()
|
|
|
|
|
|
|
|
|
|
|
|
# TODO, have this read names in from a file
|
|
|
|
class ChessEndScreen(PatreonEndScreen):
|
|
|
|
CONFIG = {
|
|
|
|
"scroll_time": 20,
|
|
|
|
}
|