mirror of
https://github.com/3b1b/manim.git
synced 2025-08-05 16:49:03 +00:00
Poker hand scene of eop/bayes
This commit is contained in:
parent
fd567bb0f4
commit
c4b4acc69b
3 changed files with 291 additions and 35 deletions
173
eop/bayes.py
173
eop/bayes.py
|
@ -40,21 +40,23 @@ class BayesOpeningQuote(OpeningQuote):
|
||||||
"author" : "Dennis V. Lindley",
|
"author" : "Dennis V. Lindley",
|
||||||
}
|
}
|
||||||
|
|
||||||
class IntroducePokerHand(PiCreatureScene):
|
class IntroducePokerHand(PiCreatureScene, SampleSpaceScene):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
"community_cards_center" : 1.5*DOWN,
|
"community_cards_center" : 1.5*DOWN,
|
||||||
"community_card_values" : ["AS", "QH", "10H", "2C", "5H"],
|
"community_card_values" : ["AS", "QH", "10H", "2C", "5H"],
|
||||||
"your_hand_values" : ["JS", "KC"],
|
"your_hand_values" : ["JS", "KC"],
|
||||||
}
|
}
|
||||||
def construct(self):
|
def construct(self):
|
||||||
self.force_skipping()
|
|
||||||
|
|
||||||
self.add_cards()
|
self.add_cards()
|
||||||
# self.indicate_straight()
|
self.indicate_straight()
|
||||||
self.show_flush_potential()
|
self.show_flush_potential()
|
||||||
self.compute_flush_probability()
|
self.compute_flush_probability()
|
||||||
self.show_flush_sample_space()
|
self.show_flush_sample_space()
|
||||||
|
self.talk_through_sample_space()
|
||||||
self.place_high_bet()
|
self.place_high_bet()
|
||||||
|
self.change_belief()
|
||||||
|
self.move_community_cards_out_of_the_way()
|
||||||
|
self.name_bayes_rule()
|
||||||
|
|
||||||
def add_cards(self):
|
def add_cards(self):
|
||||||
you, her = self.you, self.her
|
you, her = self.you, self.her
|
||||||
|
@ -86,6 +88,7 @@ class IntroducePokerHand(PiCreatureScene):
|
||||||
self.dither()
|
self.dither()
|
||||||
|
|
||||||
self.community_cards = community_cards
|
self.community_cards = community_cards
|
||||||
|
self.deck = deck
|
||||||
|
|
||||||
def indicate_straight(self):
|
def indicate_straight(self):
|
||||||
you = self.you
|
you = self.you
|
||||||
|
@ -234,7 +237,6 @@ class IntroducePokerHand(PiCreatureScene):
|
||||||
color = BLUE, buff = SMALL_BUFF
|
color = BLUE, buff = SMALL_BUFF
|
||||||
)
|
)
|
||||||
|
|
||||||
self.revert_to_original_skipping_status()
|
|
||||||
self.play(LaggedStart(FadeIn, equation))
|
self.play(LaggedStart(FadeIn, equation))
|
||||||
self.dither(2)
|
self.dither(2)
|
||||||
self.play(
|
self.play(
|
||||||
|
@ -262,23 +264,159 @@ class IntroducePokerHand(PiCreatureScene):
|
||||||
you, her = self.you, self.her
|
you, her = self.you, self.her
|
||||||
percentage = self.percentage
|
percentage = self.percentage
|
||||||
|
|
||||||
sample_space = SampleSpace()
|
sample_space = self.get_sample_space()
|
||||||
sample_space.add_title()
|
sample_space.add_title("Your belief")
|
||||||
sample_space.move_to(VGroup(you.hand, her.hand))
|
sample_space.move_to(VGroup(you.hand, her.hand))
|
||||||
sample_space.to_edge(UP, buff = MED_SMALL_BUFF)
|
sample_space.to_edge(UP, buff = MED_SMALL_BUFF)
|
||||||
sample_space.shift(RIGHT)
|
|
||||||
p = 1./22
|
p = 1./22
|
||||||
top_part, bottom_part = sample_space.divide_horizontally(
|
sample_space.divide_horizontally(
|
||||||
p, colors = [SuitSymbol.CONFIG["red"], BLUE_E]
|
p, colors = [SuitSymbol.CONFIG["red"], BLUE_E]
|
||||||
)
|
)
|
||||||
|
top_label, bottom_label = sample_space.get_side_labels([
|
||||||
|
percentage.get_tex_string(), "95.5\\%"
|
||||||
|
])
|
||||||
|
|
||||||
self.revert_to_original_skipping_status()
|
self.play(
|
||||||
self.play(FadeIn(sample_space))
|
FadeIn(sample_space),
|
||||||
|
ReplacementTransform(percentage, top_label[1])
|
||||||
|
)
|
||||||
|
self.play(*map(GrowFromCenter, [
|
||||||
|
label[0] for label in top_label, bottom_label
|
||||||
|
]))
|
||||||
|
self.dither(2)
|
||||||
|
self.play(Write(bottom_label[1]))
|
||||||
|
self.dither(2)
|
||||||
|
|
||||||
|
self.sample_space = sample_space
|
||||||
|
|
||||||
|
def talk_through_sample_space(self):
|
||||||
|
her = self.her
|
||||||
|
sample_space = self.sample_space
|
||||||
|
top_part, bottom_part = self.sample_space.horizontal_parts
|
||||||
|
|
||||||
|
flush_hands, non_flush_hands = hand_lists = [
|
||||||
|
[self.get_hand(her, keys) for keys in key_list]
|
||||||
|
for key_list in [
|
||||||
|
[("3H", "8H"), ("4H", "AH"), ("JH", "KH")],
|
||||||
|
[("AC", "6D"), ("3D", "6S"), ("JH", "4C")],
|
||||||
|
]
|
||||||
|
]
|
||||||
|
for hand_list, part in zip(hand_lists, [top_part, bottom_part]):
|
||||||
|
self.play(Indicate(part, scale_factor = 1))
|
||||||
|
for hand in hand_list:
|
||||||
|
hand.save_state()
|
||||||
|
hand.scale(0.01)
|
||||||
|
hand.move_to(part.get_right())
|
||||||
|
self.play(hand.restore)
|
||||||
|
self.dither()
|
||||||
self.dither()
|
self.dither()
|
||||||
self.add(top_part, bottom_part)
|
self.play(*map(FadeOut, it.chain(*hand_lists)))
|
||||||
|
|
||||||
def place_high_bet(self):
|
def place_high_bet(self):
|
||||||
pass
|
you, her = self.you, self.her
|
||||||
|
pre_money = VGroup(*[
|
||||||
|
VGroup(*[
|
||||||
|
TexMobject("\\$")
|
||||||
|
for x in range(10)
|
||||||
|
]).arrange_submobjects(RIGHT, buff = SMALL_BUFF)
|
||||||
|
for y in range(4)
|
||||||
|
]).arrange_submobjects(UP, buff = SMALL_BUFF)
|
||||||
|
money = VGroup(*it.chain(*pre_money))
|
||||||
|
money.highlight(GREEN)
|
||||||
|
money.scale(0.8)
|
||||||
|
money.next_to(her.hand, DOWN)
|
||||||
|
for dollar in money:
|
||||||
|
dollar.save_state()
|
||||||
|
dollar.scale(0.01)
|
||||||
|
dollar.move_to(her.get_boundary_point(RIGHT))
|
||||||
|
dollar.set_fill(opacity = 0)
|
||||||
|
|
||||||
|
self.play(LaggedStart(
|
||||||
|
ApplyMethod,
|
||||||
|
money,
|
||||||
|
lambda m : (m.restore,),
|
||||||
|
run_time = 5,
|
||||||
|
))
|
||||||
|
self.play(you.change_mode, "confused")
|
||||||
|
self.dither()
|
||||||
|
|
||||||
|
self.money = money
|
||||||
|
|
||||||
|
def change_belief(self):
|
||||||
|
numbers = VGroup(*[
|
||||||
|
label[1]
|
||||||
|
for label in self.sample_space.horizontal_parts.labels
|
||||||
|
])
|
||||||
|
rect = Rectangle(stroke_width = 0)
|
||||||
|
rect.set_fill(BLACK, 1)
|
||||||
|
rect.stretch_to_fit_width(numbers.get_width())
|
||||||
|
rect.stretch_to_fit_height(self.sample_space.get_height())
|
||||||
|
rect.move_to(numbers, UP)
|
||||||
|
|
||||||
|
self.play(FadeIn(rect))
|
||||||
|
self.change_horizontal_division(
|
||||||
|
0.2,
|
||||||
|
run_time = 3,
|
||||||
|
rate_func = there_and_back,
|
||||||
|
added_anims = [Animation(rect)]
|
||||||
|
)
|
||||||
|
self.play(FadeOut(rect))
|
||||||
|
|
||||||
|
def move_community_cards_out_of_the_way(self):
|
||||||
|
cards = self.community_cards
|
||||||
|
cards.generate_target()
|
||||||
|
cards.target.arrange_submobjects(
|
||||||
|
RIGHT, buff = -cards[0].get_width() + MED_SMALL_BUFF,
|
||||||
|
)
|
||||||
|
cards.target.move_to(self.deck)
|
||||||
|
cards.target.to_edge(LEFT)
|
||||||
|
|
||||||
|
self.sample_space.add(self.sample_space.horizontal_parts.labels)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
self.deck.scale, 0.7,
|
||||||
|
self.deck.next_to, cards.target, UP,
|
||||||
|
self.deck.to_edge, LEFT,
|
||||||
|
self.sample_space.shift, 3*DOWN,
|
||||||
|
MoveToTarget(cards)
|
||||||
|
)
|
||||||
|
|
||||||
|
def name_bayes_rule(self):
|
||||||
|
title = TextMobject("Bayes' rule")
|
||||||
|
title.highlight(BLUE)
|
||||||
|
title.to_edge(UP)
|
||||||
|
subtitle = TextMobject("Update ", "prior ", "beliefs")
|
||||||
|
subtitle.scale(0.8)
|
||||||
|
subtitle.next_to(title, DOWN)
|
||||||
|
prior_word = subtitle.get_part_by_tex("prior")
|
||||||
|
numbers = VGroup(*[
|
||||||
|
label[1] for label in self.sample_space.horizontal_parts.labels
|
||||||
|
])
|
||||||
|
rect = SurroundingRectangle(numbers, color = GREEN)
|
||||||
|
arrow = Arrow(prior_word.get_bottom(), rect.get_top())
|
||||||
|
arrow.highlight(GREEN)
|
||||||
|
|
||||||
|
words = TextMobject(
|
||||||
|
"Maybe she really \\\\ does have a flush $\\dots$",
|
||||||
|
alignment = ""
|
||||||
|
)
|
||||||
|
words.scale(0.7)
|
||||||
|
words.next_to(self.money, DOWN, aligned_edge = LEFT)
|
||||||
|
|
||||||
|
self.play(
|
||||||
|
Write(title, run_time = 2),
|
||||||
|
self.you.change_mode, "pondering"
|
||||||
|
)
|
||||||
|
self.dither()
|
||||||
|
self.play(FadeIn(subtitle))
|
||||||
|
self.play(prior_word.highlight, GREEN)
|
||||||
|
self.play(
|
||||||
|
ShowCreation(rect),
|
||||||
|
ShowCreation(arrow)
|
||||||
|
)
|
||||||
|
self.dither(3)
|
||||||
|
self.play(Write(words))
|
||||||
|
self.dither(3)
|
||||||
|
|
||||||
|
|
||||||
######
|
######
|
||||||
|
@ -337,9 +475,14 @@ class HowDoesPokerWork(TeacherStudentsScene):
|
||||||
self.change_student_modes(*["confused"]*3)
|
self.change_student_modes(*["confused"]*3)
|
||||||
self.dither(2)
|
self.dither(2)
|
||||||
|
|
||||||
class ShowCountingArgument(IntroducePokerHand):
|
class YourGutKnowsBayesRule(TeacherStudentsScene):
|
||||||
def construct(self):
|
def construct(self):
|
||||||
pass
|
self.teacher_says(
|
||||||
|
"Your gut knows \\\\ Bayes' rule.",
|
||||||
|
run_time = 1
|
||||||
|
)
|
||||||
|
self.change_student_modes("confused", "gracious", "guilty")
|
||||||
|
self.dither(3)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -63,7 +63,6 @@ class TexMobject(SVGMobject):
|
||||||
#specialized path_string mobject
|
#specialized path_string mobject
|
||||||
return TexSymbol(path_string)
|
return TexSymbol(path_string)
|
||||||
|
|
||||||
|
|
||||||
def generate_points(self):
|
def generate_points(self):
|
||||||
SVGMobject.generate_points(self)
|
SVGMobject.generate_points(self)
|
||||||
if len(self.args) > 1:
|
if len(self.args) > 1:
|
||||||
|
|
|
@ -1,12 +1,52 @@
|
||||||
from helpers import *
|
from helpers import *
|
||||||
|
|
||||||
|
from scene import Scene
|
||||||
|
|
||||||
|
from animation.animation import Animation
|
||||||
|
from animation.transform import Transform
|
||||||
|
|
||||||
from mobject import Mobject
|
from mobject import Mobject
|
||||||
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint
|
||||||
from mobject.svg_mobject import SVGMobject
|
from mobject.svg_mobject import SVGMobject
|
||||||
from mobject.tex_mobject import TextMobject, TexMobject
|
from mobject.tex_mobject import TextMobject, TexMobject, Brace
|
||||||
|
|
||||||
from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon
|
from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon
|
||||||
|
|
||||||
|
EPSILON = 0.0001
|
||||||
|
|
||||||
|
class SampleSpaceScene(Scene):
|
||||||
|
def get_sample_space(self, **config):
|
||||||
|
self.sample_space = SampleSpace(**config)
|
||||||
|
return self.sample_space
|
||||||
|
|
||||||
|
def add_sample_space(self, **config):
|
||||||
|
self.add(self.get_sample_space(**config))
|
||||||
|
|
||||||
|
def change_horizontal_division(self, p_list, **kwargs):
|
||||||
|
assert(hasattr(self.sample_space, "horizontal_parts"))
|
||||||
|
added_anims = kwargs.pop("added_anims", [])
|
||||||
|
new_division_kwargs = kwargs.pop("new_division_kwargs", {})
|
||||||
|
added_label_kwargs = kwargs.pop("label_kwargs", {})
|
||||||
|
|
||||||
|
curr_parts = self.sample_space.horizontal_parts
|
||||||
|
new_division_kwargs["colors"] = [
|
||||||
|
part.get_color() for part in curr_parts
|
||||||
|
]
|
||||||
|
new_parts = self.sample_space.get_horizontal_division(
|
||||||
|
p_list, **new_division_kwargs
|
||||||
|
)
|
||||||
|
anims = [Transform(curr_parts, new_parts)]
|
||||||
|
if hasattr(curr_parts, "labels"):
|
||||||
|
label_kwargs = curr_parts.label_kwargs
|
||||||
|
label_kwargs.update(added_label_kwargs)
|
||||||
|
new_labels = self.sample_space.get_subdivision_labels(
|
||||||
|
new_parts, **label_kwargs
|
||||||
|
)
|
||||||
|
anims.append(Transform(curr_parts.labels, new_labels))
|
||||||
|
anims += added_anims
|
||||||
|
|
||||||
|
self.play(*anims, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class SampleSpace(VGroup):
|
class SampleSpace(VGroup):
|
||||||
CONFIG = {
|
CONFIG = {
|
||||||
|
@ -16,7 +56,8 @@ class SampleSpace(VGroup):
|
||||||
"fill_color" : DARK_GREY,
|
"fill_color" : DARK_GREY,
|
||||||
"fill_opacity" : 0.8,
|
"fill_opacity" : 0.8,
|
||||||
"stroke_width" : 0,
|
"stroke_width" : 0,
|
||||||
}
|
},
|
||||||
|
"default_label_scale_val" : 0.7,
|
||||||
}
|
}
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
VGroup.__init__(self, **kwargs)
|
VGroup.__init__(self, **kwargs)
|
||||||
|
@ -32,30 +73,83 @@ class SampleSpace(VGroup):
|
||||||
self.title = title_mob
|
self.title = title_mob
|
||||||
self.add(title_mob)
|
self.add(title_mob)
|
||||||
|
|
||||||
def divide_along_dimension(self, p, dim, colors):
|
def add_label(self, label):
|
||||||
|
self.label = label
|
||||||
|
|
||||||
|
def get_division_along_dimension(self, p_list, dim, colors, vect):
|
||||||
|
p_list = list(tuplify(p_list))
|
||||||
|
if abs(1.0 - sum(p_list)) > EPSILON:
|
||||||
|
p_list.append(1.0 - sum(p_list))
|
||||||
|
colors = color_gradient(colors, len(p_list))
|
||||||
perp_dim = 1-dim
|
perp_dim = 1-dim
|
||||||
if dim == 0:
|
|
||||||
vects = [UP, DOWN]
|
last_point = self.full_space.get_edge_center(-vect)
|
||||||
else:
|
|
||||||
vects = [LEFT, RIGHT]
|
|
||||||
parts = VGroup()
|
parts = VGroup()
|
||||||
for factor, vect, color in zip([p, 1-p], vects, colors):
|
for factor, color in zip(p_list, colors):
|
||||||
part = self.full_space.copy()
|
part = SampleSpace()
|
||||||
part.set_fill(color, 1)
|
part.set_fill(color, 1)
|
||||||
|
part.replace(self.full_space, stretch = True)
|
||||||
part.stretch(factor, perp_dim)
|
part.stretch(factor, perp_dim)
|
||||||
part.move_to(self.full_space, vect)
|
part.move_to(last_point, -vect)
|
||||||
|
last_point = part.get_edge_center(vect)
|
||||||
parts.add(part)
|
parts.add(part)
|
||||||
return parts
|
return parts
|
||||||
|
|
||||||
def divide_horizontally(self, p, colors = [GREEN_E, RED_E]):
|
def get_horizontal_division(
|
||||||
result = self.divide_along_dimension(p, 0, colors)
|
self, p_list,
|
||||||
self.top_part, self.bottom_part = result
|
colors = [GREEN_E, BLUE],
|
||||||
return result
|
vect = DOWN
|
||||||
|
):
|
||||||
|
return self.get_division_along_dimension(p_list, 0, colors, vect)
|
||||||
|
|
||||||
|
def get_vertical_division(
|
||||||
|
self, p_list,
|
||||||
|
colors = [MAROON_B, YELLOW],
|
||||||
|
vect = RIGHT
|
||||||
|
):
|
||||||
|
return self.get_division_along_dimension(p_list, 1, colors, vect)
|
||||||
|
|
||||||
|
def divide_horizontally(self, *args, **kwargs):
|
||||||
|
self.horizontal_parts = self.get_horizontal_division(*args, **kwargs)
|
||||||
|
self.add(self.horizontal_parts)
|
||||||
|
|
||||||
|
def divide_vertically(self, *args, **kwargs):
|
||||||
|
self.vertical_parts = self.get_vertical_division(*args, **kwargs)
|
||||||
|
self.add(self.vertical_parts)
|
||||||
|
|
||||||
|
def get_subdivision_labels(self, parts, labels, direction, buff = SMALL_BUFF):
|
||||||
|
label_brace_groups = VGroup()
|
||||||
|
for label, part in zip(labels, parts):
|
||||||
|
brace = Brace(part, direction, min_num_quads = 1, buff = buff)
|
||||||
|
label_mob = TexMobject(label)
|
||||||
|
label_mob.scale(self.default_label_scale_val)
|
||||||
|
label_mob.next_to(brace, direction, buff)
|
||||||
|
full_label = VGroup(brace, label_mob)
|
||||||
|
part.add_label(full_label)
|
||||||
|
label_brace_groups.add(full_label)
|
||||||
|
parts.labels = label_brace_groups
|
||||||
|
parts.label_kwargs = {
|
||||||
|
"labels" : labels,
|
||||||
|
"direction" : direction,
|
||||||
|
"buff" : buff,
|
||||||
|
}
|
||||||
|
return label_brace_groups
|
||||||
|
|
||||||
|
def get_side_labels(self, labels, direction = LEFT, **kwargs):
|
||||||
|
assert(hasattr(self, "horizontal_parts"))
|
||||||
|
parts = self.horizontal_parts
|
||||||
|
return self.get_subdivision_labels(parts, labels, direction, **kwargs)
|
||||||
|
|
||||||
|
def get_top_labels(self, labels, **kwargs):
|
||||||
|
assert(hasattr(self, "vertical_parts"))
|
||||||
|
parts = self.vertical_parts
|
||||||
|
return self.get_subdivision_labels(parts, labels, UP, **kwargs)
|
||||||
|
|
||||||
|
def get_bototm_labels(self, labels, **kwargs):
|
||||||
|
assert(hasattr(self, "vertical_parts"))
|
||||||
|
parts = self.vertical_parts
|
||||||
|
return self.get_subdivision_labels(parts, labels, DOWN, **kwargs)
|
||||||
|
|
||||||
def divide_vertically(self, p, colors = [MAROON_B, YELLOW]):
|
|
||||||
result = self.divide_along_dimension(p, 1, colors)
|
|
||||||
self.left_part, self.right_part = result
|
|
||||||
return result
|
|
||||||
|
|
||||||
### Cards ###
|
### Cards ###
|
||||||
|
|
||||||
|
@ -225,7 +319,8 @@ class PlayingCard(VGroup):
|
||||||
)
|
)
|
||||||
sub_rect.move_to(self)
|
sub_rect.move_to(self)
|
||||||
|
|
||||||
pi_color = average_color(symbol.get_color(), GREY)
|
# pi_color = average_color(symbol.get_color(), GREY)
|
||||||
|
pi_color = symbol.get_color()
|
||||||
pi_mode = {
|
pi_mode = {
|
||||||
"J" : "plain",
|
"J" : "plain",
|
||||||
"Q" : "thinking",
|
"Q" : "thinking",
|
||||||
|
@ -305,3 +400,22 @@ class SuitSymbol(SVGMobject):
|
||||||
self.set_stroke(width = 0)
|
self.set_stroke(width = 0)
|
||||||
self.set_fill(color, 1)
|
self.set_fill(color, 1)
|
||||||
self.scale_to_fit_height(self.height)
|
self.scale_to_fit_height(self.height)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue