diff --git a/eop/__init__.py b/eop/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/eop/bayes.py b/eop/bayes.py new file mode 100644 index 00000000..8c57a9a5 --- /dev/null +++ b/eop/bayes.py @@ -0,0 +1,369 @@ +from helpers import * + +from mobject.tex_mobject import TexMobject +from mobject import Mobject +from mobject.image_mobject import ImageMobject +from mobject.vectorized_mobject import * + +from animation.animation import Animation +from animation.transform import * +from animation.simple_animations import * +from animation.playground import * +from topics.geometry import * +from topics.characters import * +from topics.functions import * +from topics.fractals import * +from topics.number_line import * +from topics.combinatorics import * +from topics.numerals import * +from topics.three_dimensions import * +from topics.objects import * +from topics.complex_numbers import * +from topics.common_scenes import * +from topics.probability import * +from scene import Scene +from scene.reconfigurable_scene import ReconfigurableScene +from scene.zoomed_scene import * +from camera import Camera +from mobject.svg_mobject import * +from mobject.tex_mobject import * + +#revert_to_original_skipping_status + +######### + +class BayesOpeningQuote(OpeningQuote): + CONFIG = { + "quote" : [ + "Inside every non-Bayesian there \\\\ is a Bayesian struggling to get out." + ], + "author" : "Dennis V. Lindley", + } + +class IntroducePokerHand(PiCreatureScene): + CONFIG = { + "community_cards_center" : 1.5*DOWN, + "community_card_values" : ["AS", "QH", "10H", "2C", "5H"], + "your_hand_values" : ["JS", "KC"], + } + def construct(self): + self.force_skipping() + + self.add_cards() + # self.indicate_straight() + self.show_flush_potential() + self.compute_flush_probability() + self.show_flush_sample_space() + self.place_high_bet() + + def add_cards(self): + you, her = self.you, self.her + community_cards = VGroup(*map( + PlayingCard, self.community_card_values + )) + community_cards.arrange_submobjects(RIGHT) + community_cards.move_to(self.community_cards_center) + deck = VGroup(*[ + PlayingCard(turned_over = True) + for x in range(5) + ]) + for i, card in enumerate(deck): + card.shift(i*(0.03*RIGHT + 0.015*DOWN)) + deck.next_to(community_cards, LEFT) + + you.hand = self.get_hand(you, self.your_hand_values) + her.hand = self.get_hand(her, None) + hand_cards = VGroup(*it.chain(*zip(you.hand, her.hand))) + + self.add(deck) + for group in hand_cards, community_cards: + for card in group: + card.generate_target() + card.scale(0.01) + card.move_to(deck[-1], UP+RIGHT) + self.play(LaggedStart(MoveToTarget, group, lag_ratio = 0.8)) + self.dither() + self.dither() + + self.community_cards = community_cards + + def indicate_straight(self): + you = self.you + community_cards = self.community_cards + you.hand.save_state() + you.hand.generate_target() + for card in you.hand.target: + card.scale_to_fit_height(community_cards.get_height()) + + selected_community_cards = VGroup(*filter( + lambda card : card.numerical_value >= 10, + community_cards + )) + card_cmp = lambda c1, c2 : cmp( + c1.numerical_value, c2.numerical_value + ) + selected_community_cards.submobjects.sort(card_cmp) + + selected_community_cards.save_state() + for card in selected_community_cards: + card.generate_target() + + straight_cards = VGroup(*it.chain( + you.hand.target, + [c.target for c in selected_community_cards] + )) + straight_cards.submobjects.sort(card_cmp) + straight_cards.arrange_submobjects(RIGHT, buff = SMALL_BUFF) + straight_cards.next_to(community_cards, UP, aligned_edge = LEFT) + + self.play(LaggedStart( + MoveToTarget, + selected_community_cards, + run_time = 1.5 + )) + self.play(MoveToTarget(you.hand)) + self.play(you.change, "hooray", straight_cards) + self.play(LaggedStart( + ApplyMethod, + straight_cards, + lambda m : (m.highlight, YELLOW), + rate_func = there_and_back, + run_time = 1.5, + lag_ratio = 0.5, + remover = True, + )) + self.dither(2) + self.play( + selected_community_cards.restore, + you.hand.restore, + you.change_mode, "happy" + ) + + def show_flush_potential(self): + you, her = self.you, self.her + heart_cards = VGroup(*filter( + lambda c : c.suit == "hearts", + self.community_cards + )) + heart_cards.save_state() + + her.hand.save_state() + her.hand.generate_target() + her.hand.target.arrange_submobjects(RIGHT) + her.hand.target.next_to(heart_cards, UP) + her.hand.target.to_edge(UP) + + heart_qs = VGroup() + hearts = VGroup() + q_marks = VGroup() + for target in her.hand.target: + heart = SuitSymbol("hearts") + q_mark = TexMobject("?") + heart_q = VGroup(heart, q_mark) + for mob in heart_q: + mob.scale_to_fit_height(0.5) + heart_q.arrange_submobjects(RIGHT, buff = SMALL_BUFF) + heart_q.move_to(target) + heart_qs.add(heart, q_mark) + hearts.add(heart) + q_marks.add(q_mark) + + self.play(heart_cards.shift, heart_cards.get_height()*UP) + self.play(you.change_mode, "hesitant") + self.play(MoveToTarget(her.hand)) + self.play(LaggedStart(DrawBorderThenFill, heart_qs)) + self.play( + her.change, "happy", + DrawBorderThenFill(her.glasses) + ) + self.pi_creatures.remove(her) + new_suit_pairs = [ + ("clubs", "diamonds"), + ("diamonds", "spades"), + ("spades", "clubs"), + ("hearts", "hearts"), + ] + for new_suit_pair in new_suit_pairs: + new_symbols = VGroup(*map(SuitSymbol, new_suit_pair)) + for new_symbol, heart in zip(new_symbols, hearts): + new_symbol.replace(heart, dim_to_match = 1) + self.play(Transform( + hearts, new_symbols, + submobject_mode = "lagged_start" + )) + self.dither() + self.play(FadeOut(heart_qs)) + self.play( + heart_cards.restore, + her.hand.restore, + you.change_mode, "pondering", + ) + + self.q_marks = q_marks + + def compute_flush_probability(self): + you, her = self.you, self.her + equation = TexMobject( + "{ {10 \\choose 2}", "\\over", "{45 \\choose 2} }", + "=", "{1 \\over 22}", "\\approx", "4.5\\%" + ) + equation.next_to(self.community_cards, UP, buff = LARGE_BUFF) + percentage = equation.get_part_by_tex("4.5") + + ten = VGroup(*equation[0][1:3]) + num_hearts = TextMobject("\\# Remaining hearts") + num_hearts.scale(0.75) + num_hearts.next_to( + ten, UP, aligned_edge = LEFT + ) + num_hearts.to_edge(UP) + num_hearts.highlight(RED) + num_hearts_arrow = Arrow( + num_hearts.get_bottom(), ten.get_right(), + color = RED, buff = SMALL_BUFF + ) + + fourty_five = VGroup(*equation[2][1:3]) + num_cards = TextMobject("\\# Remaining cards") + num_cards.scale(0.75) + num_cards.next_to(fourty_five, LEFT) + num_cards.to_edge(LEFT) + num_cards.highlight(BLUE) + num_cards_arrow = Arrow( + num_cards, fourty_five, + color = BLUE, buff = SMALL_BUFF + ) + + self.revert_to_original_skipping_status() + self.play(LaggedStart(FadeIn, equation)) + self.dither(2) + self.play( + FadeIn(num_hearts), + ShowCreation(num_hearts_arrow), + ten.highlight, RED, + ) + self.play( + FadeIn(num_cards), + ShowCreation(num_cards_arrow), + fourty_five.highlight, BLUE + ) + self.dither(3) + equation.remove(percentage) + self.play(*map(FadeOut, [ + equation, + num_hearts, num_hearts_arrow, + num_cards, num_cards_arrow, + ])) + + + self.percentage = percentage + + def show_flush_sample_space(self): + you, her = self.you, self.her + percentage = self.percentage + + sample_space = SampleSpace() + sample_space.add_title() + sample_space.move_to(VGroup(you.hand, her.hand)) + sample_space.to_edge(UP, buff = MED_SMALL_BUFF) + sample_space.shift(RIGHT) + p = 1./22 + top_part, bottom_part = sample_space.divide_horizontally( + p, colors = [SuitSymbol.CONFIG["red"], BLUE_E] + ) + + self.revert_to_original_skipping_status() + self.play(FadeIn(sample_space)) + self.dither() + self.add(top_part, bottom_part) + + def place_high_bet(self): + pass + + + ###### + + def create_pi_creatures(self): + shift_val = 3 + you = PiCreature(color = BLUE_D) + her = PiCreature(color = BLUE_B).flip() + for pi in you, her: + pi.scale(0.5) + you.to_corner(UP+LEFT) + her.to_corner(UP+RIGHT) + you.make_eye_contact(her) + + glasses = SVGMobject(file_name = "sunglasses") + glasses.set_stroke(WHITE, width = 0) + glasses.set_fill(GREY, 1) + glasses.scale_to_fit_width( + 1.1*her.eyes.get_width() + ) + glasses.move_to(her.eyes, UP) + + her.glasses = glasses + self.you = you + self.her = her + return VGroup(you, her) + + def get_hand(self, pi_creature, keys = None): + if keys is not None: + hand = VGroup(*map(PlayingCard, keys)) + else: + hand = VGroup(*[ + PlayingCard(turned_over = True) + for x in range(2) + ]) + hand.scale(0.7) + card1, card2 = hand + vect = np.sign(pi_creature.get_center()[0])*LEFT + card2.move_to(card1) + card2.shift(MED_SMALL_BUFF*RIGHT + SMALL_BUFF*DOWN) + hand.next_to( + pi_creature, vect, + buff = MED_LARGE_BUFF, + aligned_edge = UP + ) + return hand + + +class HowDoesPokerWork(TeacherStudentsScene): + def construct(self): + self.student_says( + "Wait, how does \\\\ poker work again?", + target_mode = "confused", + run_time = 1 + ) + self.change_student_modes(*["confused"]*3) + self.dither(2) + +class ShowCountingArgument(IntroducePokerHand): + def construct(self): + pass + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extract_scene.py b/extract_scene.py index b26d5346..928ef510 100644 --- a/extract_scene.py +++ b/extract_scene.py @@ -186,8 +186,8 @@ def main(): try: handle_scene(SceneClass(**scene_kwargs), **config) play_finish_sound() - except RuntimeError: - play_finish_sound() + # except RuntimeError as e: + # play_finish_sound() except: print "\n\n" traceback.print_exc() diff --git a/mobject/svg_mobject.py b/mobject/svg_mobject.py index f349b4f8..9bc77615 100644 --- a/mobject/svg_mobject.py +++ b/mobject/svg_mobject.py @@ -20,6 +20,7 @@ class SVGMobject(VMobject): "should_center" : True, #Must be filled in in a subclass, or when called "file_name" : None, + "propogate_style_to_family" : True, } def __init__(self, **kwargs): digest_config(self, kwargs, locals()) diff --git a/topics/objects.py b/topics/objects.py index 522ace18..842310e3 100644 --- a/topics/objects.py +++ b/topics/objects.py @@ -1,7 +1,7 @@ from helpers import * from mobject import Mobject -from mobject.vectorized_mobject import VGroup, VMobject +from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint from mobject.svg_mobject import SVGMobject from mobject.tex_mobject import TextMobject, TexMobject @@ -26,13 +26,21 @@ class PlayingCard(VGroup): CONFIG = { "value" : None, "suit" : None, - "height" : 1.5, + "key" : None, ##String like "8H" or "KS" + "height" : 2, "height_to_width" : 3.5/2.5, "card_height_to_symbol_height" : 7, "card_width_to_corner_num_width" : 10, "card_height_to_corner_num_height" : 10, "color" : LIGHT_GREY, + "turned_over" : False, + "possible_suits" : ["hearts", "diamonds", "spades", "clubs"], + "possible_values" : map(str, range(2, 11)) + ["J", "Q", "K", "A"], } + + def __init__(self, key = None, **kwargs): + VGroup.__init__(self, key = key, **kwargs) + def generate_points(self): self.add(Rectangle( height = self.height, @@ -42,32 +50,58 @@ class PlayingCard(VGroup): fill_color = self.color, fill_opacity = 1, )) - value = self.get_value() - symbol = self.get_symbol() - design = self.get_design(value, symbol) - corner_numbers = self.get_corner_numbers(value, symbol) - self.add(design, corner_numbers) + if self.turned_over: + self.set_fill(DARK_GREY) + self.set_stroke(LIGHT_GREY) + contents = VectorizedPoint(self.get_center()) + else: + value = self.get_value() + symbol = self.get_symbol() + design = self.get_design(value, symbol) + corner_numbers = self.get_corner_numbers(value, symbol) + contents = VGroup(design, corner_numbers) + self.design = design + self.corner_numbers = corner_numbers + self.add(contents) def get_value(self): value = self.value - possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] if value is None: - value = random.choice(possible_values) - value = str(value) - if value not in possible_values: - raise Exception("Invalid card value") - value = value.capitalize() + if self.key is not None: + value = self.key[:-1] + else: + value = random.choice(self.possible_values) + value = string.upper(str(value)) if value == "1": value = "A" + if value not in self.possible_values: + raise Exception("Invalid card value") + + face_card_to_value = { + "J" : 11, + "Q" : 12, + "K" : 13, + "A" : 14, + } + try: + self.numerical_value = int(value) + except: + self.numerical_value = face_card_to_value[value] return value def get_symbol(self): suit = self.suit - possible_suits = ["hearts", "diamonds", "spades", "clubs"] if suit is None: - suit = random.choice(possible_suits) - if suit not in possible_suits: + if self.key is not None: + suit = dict([ + (string.upper(s[0]), s) + for s in self.possible_suits + ])[string.upper(self.key[-1])] + else: + suit = random.choice(self.possible_suits) + if suit not in self.possible_suits: raise Exception("Invalud suit value") + self.suit = suit symbol_height = float(self.height) / self.card_height_to_symbol_height symbol = SuitSymbol(suit, height = symbol_height) return symbol @@ -225,7 +259,6 @@ class SuitSymbol(SVGMobject): self.set_fill(color, 1) self.scale_to_fit_height(self.height) - class Speedometer(VMobject): CONFIG = { "arc_angle" : 4*np.pi/3, diff --git a/topics/probability.py b/topics/probability.py new file mode 100644 index 00000000..ccfd23bb --- /dev/null +++ b/topics/probability.py @@ -0,0 +1,307 @@ +from helpers import * + +from mobject import Mobject +from mobject.vectorized_mobject import VGroup, VMobject, VectorizedPoint +from mobject.svg_mobject import SVGMobject +from mobject.tex_mobject import TextMobject, TexMobject + +from topics.geometry import Circle, Line, Rectangle, Square, Arc, Polygon + + +class SampleSpace(VGroup): + CONFIG = { + "full_space_config" : { + "height" : 3, + "width" : 3, + "fill_color" : DARK_GREY, + "fill_opacity" : 0.8, + "stroke_width" : 0, + } + } + def __init__(self, **kwargs): + VGroup.__init__(self, **kwargs) + full_space = Rectangle(**self.full_space_config) + self.full_space = full_space + self.add(full_space) + + def add_title(self, title = "Sample space", buff = MED_SMALL_BUFF): + title_mob = TextMobject(title) + if title_mob.get_width() > self.get_width(): + title_mob.scale_to_fit_width(self.get_width()) + title_mob.next_to(self.full_space, UP, buff = buff) + self.title = title_mob + self.add(title_mob) + + def divide_along_dimension(self, p, dim, colors): + perp_dim = 1-dim + if dim == 0: + vects = [UP, DOWN] + else: + vects = [LEFT, RIGHT] + parts = VGroup() + for factor, vect, color in zip([p, 1-p], vects, colors): + part = self.full_space.copy() + part.set_fill(color, 1) + part.stretch(factor, perp_dim) + part.move_to(self.full_space, vect) + parts.add(part) + return parts + + def divide_horizontally(self, p, colors = [GREEN_E, RED_E]): + result = self.divide_along_dimension(p, 0, colors) + self.top_part, self.bottom_part = result + return result + + 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 ### + +class DeckOfCards(VGroup): + def __init__(self, **kwargs): + possible_values = map(str, range(1, 11)) + ["J", "Q", "K"] + possible_suits = ["hearts", "diamonds", "spades", "clubs"] + VGroup.__init__(self, *[ + PlayingCard(value = value, suit = suit, **kwargs) + for value in possible_values + for suit in possible_suits + ]) + +class PlayingCard(VGroup): + CONFIG = { + "value" : None, + "suit" : None, + "key" : None, ##String like "8H" or "KS" + "height" : 2, + "height_to_width" : 3.5/2.5, + "card_height_to_symbol_height" : 7, + "card_width_to_corner_num_width" : 10, + "card_height_to_corner_num_height" : 10, + "color" : LIGHT_GREY, + "turned_over" : False, + "possible_suits" : ["hearts", "diamonds", "spades", "clubs"], + "possible_values" : map(str, range(2, 11)) + ["J", "Q", "K", "A"], + } + + def __init__(self, key = None, **kwargs): + VGroup.__init__(self, key = key, **kwargs) + + def generate_points(self): + self.add(Rectangle( + height = self.height, + width = self.height/self.height_to_width, + stroke_color = WHITE, + stroke_width = 2, + fill_color = self.color, + fill_opacity = 1, + )) + if self.turned_over: + self.set_fill(DARK_GREY) + self.set_stroke(LIGHT_GREY) + contents = VectorizedPoint(self.get_center()) + else: + value = self.get_value() + symbol = self.get_symbol() + design = self.get_design(value, symbol) + corner_numbers = self.get_corner_numbers(value, symbol) + contents = VGroup(design, corner_numbers) + self.design = design + self.corner_numbers = corner_numbers + self.add(contents) + + def get_value(self): + value = self.value + if value is None: + if self.key is not None: + value = self.key[:-1] + else: + value = random.choice(self.possible_values) + value = string.upper(str(value)) + if value == "1": + value = "A" + if value not in self.possible_values: + raise Exception("Invalid card value") + + face_card_to_value = { + "J" : 11, + "Q" : 12, + "K" : 13, + "A" : 14, + } + try: + self.numerical_value = int(value) + except: + self.numerical_value = face_card_to_value[value] + return value + + def get_symbol(self): + suit = self.suit + if suit is None: + if self.key is not None: + suit = dict([ + (string.upper(s[0]), s) + for s in self.possible_suits + ])[string.upper(self.key[-1])] + else: + suit = random.choice(self.possible_suits) + if suit not in self.possible_suits: + raise Exception("Invalud suit value") + self.suit = suit + symbol_height = float(self.height) / self.card_height_to_symbol_height + symbol = SuitSymbol(suit, height = symbol_height) + return symbol + + def get_design(self, value, symbol): + if value == "A": + return self.get_ace_design(symbol) + if value in map(str, range(2, 11)): + return self.get_number_design(value, symbol) + else: + return self.get_face_card_design(value, symbol) + + def get_ace_design(self, symbol): + design = symbol.copy().scale(1.5) + design.move_to(self) + return design + + def get_number_design(self, value, symbol): + num = int(value) + n_rows = { + 2 : 2, + 3 : 3, + 4 : 2, + 5 : 2, + 6 : 3, + 7 : 3, + 8 : 3, + 9 : 4, + 10 : 4, + }[num] + n_cols = 1 if num in [2, 3] else 2 + insertion_indices = { + 5 : [0], + 7 : [0], + 8 : [0, 1], + 9 : [1], + 10 : [0, 2], + }.get(num, []) + + top = self.get_top() + symbol.get_height()*DOWN + bottom = self.get_bottom() + symbol.get_height()*UP + column_points = [ + interpolate(top, bottom, alpha) + for alpha in np.linspace(0, 1, n_rows) + ] + + design = VGroup(*[ + symbol.copy().move_to(point) + for point in column_points + ]) + if n_cols == 2: + space = 0.2*self.get_width() + column_copy = design.copy().shift(space*RIGHT) + design.shift(space*LEFT) + design.add(*column_copy) + design.add(*[ + symbol.copy().move_to( + center_of_mass(column_points[i:i+2]) + ) + for i in insertion_indices + ]) + for symbol in design: + if symbol.get_center()[1] < self.get_center()[1]: + symbol.rotate_in_place(np.pi) + return design + + def get_face_card_design(self, value, symbol): + from topics.characters import PiCreature + sub_rect = Rectangle( + stroke_color = BLACK, + fill_opacity = 0, + height = 0.9*self.get_height(), + width = 0.6*self.get_width(), + ) + sub_rect.move_to(self) + + pi_color = average_color(symbol.get_color(), GREY) + pi_mode = { + "J" : "plain", + "Q" : "thinking", + "K" : "hooray" + }[value] + pi_creature = PiCreature( + mode = pi_mode, + color = pi_color, + ) + pi_creature.scale_to_fit_width(0.8*sub_rect.get_width()) + if value in ["Q", "K"]: + prefix = "king" if value == "K" else "queen" + crown = SVGMobject(file_name = prefix + "_crown") + crown.set_stroke(width = 0) + crown.set_fill(YELLOW, 1) + crown.stretch_to_fit_width(0.5*sub_rect.get_width()) + crown.stretch_to_fit_height(0.17*sub_rect.get_height()) + crown.move_to(pi_creature.eyes.get_center(), DOWN) + pi_creature.add_to_back(crown) + to_top_buff = 0 + else: + to_top_buff = SMALL_BUFF*sub_rect.get_height() + pi_creature.next_to(sub_rect.get_top(), DOWN, to_top_buff) + # pi_creature.shift(0.05*sub_rect.get_width()*RIGHT) + + pi_copy = pi_creature.copy() + pi_copy.rotate(np.pi, about_point = sub_rect.get_center()) + + return VGroup(sub_rect, pi_creature, pi_copy) + + def get_corner_numbers(self, value, symbol): + value_mob = TextMobject(value) + width = self.get_width()/self.card_width_to_corner_num_width + height = self.get_height()/self.card_height_to_corner_num_height + value_mob.scale_to_fit_width(width) + value_mob.stretch_to_fit_height(height) + value_mob.next_to( + self.get_corner(UP+LEFT), DOWN+RIGHT, + buff = MED_LARGE_BUFF*width + ) + value_mob.highlight(symbol.get_color()) + corner_symbol = symbol.copy() + corner_symbol.scale_to_fit_width(width) + corner_symbol.next_to( + value_mob, DOWN, + buff = MED_SMALL_BUFF*width + ) + corner_group = VGroup(value_mob, corner_symbol) + opposite_corner_group = corner_group.copy() + opposite_corner_group.rotate( + np.pi, about_point = self.get_center() + ) + + return VGroup(corner_group, opposite_corner_group) + +class SuitSymbol(SVGMobject): + CONFIG = { + "height" : 0.5, + "fill_opacity" : 1, + "stroke_width" : 0, + "red" : "#D02028", + "black" : BLACK, + } + def __init__(self, suit_name, **kwargs): + digest_config(self, kwargs) + suits_to_colors = { + "hearts" : self.red, + "diamonds" : self.red, + "spades" : self.black, + "clubs" : self.black, + } + if suit_name not in suits_to_colors: + raise Exception("Invalid suit name") + SVGMobject.__init__(self, file_name = suit_name, **kwargs) + + color = suits_to_colors[suit_name] + self.set_stroke(width = 0) + self.set_fill(color, 1) + self.scale_to_fit_height(self.height)