File size: 7,945 Bytes
f7cd020
 
7c7984f
1c3ae40
55b0da0
e7ea6a7
 
 
 
 
 
 
 
 
 
 
 
bc65520
e7ea6a7
b0e72b5
 
a135531
c10e123
b0e72b5
 
 
 
 
a135531
42d1f1b
 
1c3ae40
 
b0e72b5
 
 
 
 
1c3ae40
b0e72b5
 
 
 
1c3ae40
 
 
b0e72b5
 
d484afa
 
b0e72b5
1c3ae40
b0e72b5
 
1c3ae40
b0e72b5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55b0da0
 
a659a92
41f349c
a659a92
 
 
9ad6c07
 
 
 
 
 
 
 
 
 
b707618
a659a92
 
b707618
a659a92
2a1d221
8105381
a659a92
 
bd049e0
b707618
 
c7a90d5
b707618
 
a659a92
 
 
a135531
b707618
e7ea6a7
 
 
 
 
b707618
 
a135531
b707618
 
 
 
a135531
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a659a92
a135531
 
41f349c
 
a135531
 
 
 
a659a92
 
 
 
d192eda
 
f7cd020
d192eda
 
 
f7cd020
 
9ad6c07
 
41f349c
2de877c
ca9f937
 
 
 
 
 
8a6a672
dd32f0c
c10e123
e501b6b
a135531
 
 
d192eda
1c3ae40
 
f7cd020
 
d192eda
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# formatted with python black, line length 200

import os, random
from openai import OpenAI
import gradio as gr
from game_content import (
    INITIAL_WELCOME_TEXT,
    GAME_INTRO_CHOICES,
    NOTES_TO_THE_NARRATOR_AT_START,
    AWAITING_INPUT,
    NOTES_TO_THE_NARRATOR_EVERY_TIME_AT_FIRST,
    NOTES_TO_THE_NARRATOR_EVERY_TIME_IN_ENDGAME,
    game_over_victory_txt,
    game_over_fail_txt,
    S_GAME_OVER,
    N_TURNS_REQUIRED_TO_PASS_FIRST_BANDIT_ENCOUNTER,
    N_TURNS_REQUIRED_TO_REACH_HOME,
    AFTER_N_TURNS_MAKE_IT_EASY_TO_WIN,
)
import decider_utils
from decider_utils import YES, NO
from decider_questions import *  # QUESTION_IS_USER_HOME, QUESTION_IS_USER_ENGAGED_WITH_BANDITS, etc.
from webpage import PAGE_STYLING_JS, PLEASE_BE_PATIENT_DIV, TOP_OF_SCREEN_PADDING_DIV, MUSIC_PLAYER


N_COMPLETIONS_WHEN_ELABORATING = 1  # I previously had this set to 3, but that made the program very slow.
MINIMUM_COMPLETION_LENGTH_CHARS_WHEN_ELABORATING = 7


openai_client = OpenAI(organization=os.environ.get("OPENAI_ORGANIZATION"),
    api_key=os.environ.get("OPENAI_KEY"))


def elaborate(
    str_beginning,
    prevent_user_from_reaching_home=True,
    require_user_to_be_still_engaged_with_bandits=False,
):
    global openai_client

    longest_completion = ""

    while len(longest_completion) < MINIMUM_COMPLETION_LENGTH_CHARS_WHEN_ELABORATING:
        completions = openai_client.chat.completions.create(
            model="gpt-4-turbo",
            messages=[{"role": "system", "content": "Continue the story that begins in the next message."}, {"role": "assistant", "content": str_beginning}],
            temperature=0.5,
            max_tokens=4000 - int(len(str_beginning) / 4),
            frequency_penalty=0.8,
            presence_penalty=0.6,
            n=N_COMPLETIONS_WHEN_ELABORATING,
        ).choices

        for i in range(0, N_COMPLETIONS_WHEN_ELABORATING):
            completion = completions[i].message.content
            # debug_print(completion)

            allowed = True
            if prevent_user_from_reaching_home:
                does_the_user_reach_home = decider_utils.yesno(QUESTION_IS_USER_HOME, str_beginning + completion, default=YES)
                allowed = not does_the_user_reach_home

            if require_user_to_be_still_engaged_with_bandits:
                is_user_engaged_with_bandits = decider_utils.yesno(
                    QUESTION_IS_USER_ENGAGED_WITH_BANDITS,
                    str_beginning + completion,
                    default=YES,
                )
                allowed = allowed and is_user_engaged_with_bandits

            if allowed and len(completion) > len(longest_completion):
                longest_completion = completion

    return str_beginning + longest_completion


def run_1_game_turn(s_narr_transcript, s_n_turns_elapsed, s_user_transcript, s_user_input):
    n_turns_elapsed = int(s_n_turns_elapsed)

    finally_add2_both_tscripts = ""

    if n_turns_elapsed == 0 and s_narr_transcript == "":
        # New game.
        # I learned the hard way that we have to do the random choice thing from within the game code;
        # because if we just put it inside the "with demo:" block,
        # then the gradio server only runs the random choice once per reboot.
        game_intro = random.choice(GAME_INTRO_CHOICES)
        s_narr_transcript = game_intro + NOTES_TO_THE_NARRATOR_AT_START
        s_user_transcript = game_intro

    elif s_user_input == "":
        s_user_transcript += "You must choose an action.\n"

    elif S_GAME_OVER in s_narr_transcript:
        s_user_transcript += "Sorry, the game is already over.  To play again, please refresh the page.\n"

    elif decider_utils.special_case_is_action_lethal(s_user_input):
        finally_add2_both_tscripts += s_user_input + "\n\n"
        finally_add2_both_tscripts += game_over_fail_txt("You have taken an action that is likely to result in killing someone.")

    elif decider_utils.yesno(QUESTION_IS_USER_ENGAGED_WITH_BANDITS, s_narr_transcript, default=NO) and decider_utils.special_case_is_running_away(s_user_input):
        finally_add2_both_tscripts += "Invalid entry.  You cannot outrun these bandits.\n"

    elif decider_utils.special_case_is_magic(s_user_input):
        finally_add2_both_tscripts += "Invalid entry.  You are not a spellcaster and have no magic items except your revolver.\n"

    else:
        # User input accepted.
        n_turns_elapsed += 1
        s_user_transcript += s_user_input + "\n"
        s_narr_transcript += s_user_input + "\n"

        if n_turns_elapsed < AFTER_N_TURNS_MAKE_IT_EASY_TO_WIN:
            s_narr_transcript += NOTES_TO_THE_NARRATOR_EVERY_TIME_AT_FIRST
        else:
            s_narr_transcript += NOTES_TO_THE_NARRATOR_EVERY_TIME_IN_ENDGAME

        s_new_narr_transcript = elaborate(
            s_narr_transcript,
            prevent_user_from_reaching_home=n_turns_elapsed < N_TURNS_REQUIRED_TO_REACH_HOME,
            require_user_to_be_still_engaged_with_bandits=n_turns_elapsed < N_TURNS_REQUIRED_TO_PASS_FIRST_BANDIT_ENCOUNTER,
        )

        s_new_part = s_new_narr_transcript.replace(s_narr_transcript, "")

        s_narr_transcript += s_new_part + "\n"
        s_user_transcript += s_new_part + "\n"

        did_user_kill = decider_utils.yesno(QUESTION_DID_PROTAGONIST_KILL, s_new_part, default=NO)
        did_user_kill = did_user_kill or decider_utils.yesno(QUESTION_DID_PROTAGONIST_KILL, s_narr_transcript, default=NO)
        if did_user_kill:
            finally_add2_both_tscripts += game_over_fail_txt("You have taken a life.")

        else:
            is_user_home = decider_utils.yesno(QUESTION_IS_USER_HOME, s_narr_transcript, default=NO)
            if is_user_home:
                has_at_least_30_gold = decider_utils.yesno(QUESTION_DOES_USER_STILL_HAVE_AT_LEAST_30_GOLD, s_narr_transcript, default=NO)
                if has_at_least_30_gold:
                    finally_add2_both_tscripts += game_over_victory_txt("You made it home with 30+ gold!  Your family is grateful and you all hug in celebration.")
                else:
                    finally_add2_both_tscripts += game_over_fail_txt("You reached home with less than 30 gold - too little for your family to live on.")

        # End of code block User input accepted.

    s_n_turns_elapsed = str(n_turns_elapsed)
    s_user_input = ""

    if S_GAME_OVER not in finally_add2_both_tscripts and S_GAME_OVER not in s_narr_transcript:
        finally_add2_both_tscripts += AWAITING_INPUT

    s_narr_transcript += finally_add2_both_tscripts
    s_user_transcript += finally_add2_both_tscripts

    return [s_narr_transcript, s_n_turns_elapsed, s_user_transcript, s_user_input]



demo = gr.Blocks()

with demo:
    gr_top_of_scr_padding = gr.HTML(TOP_OF_SCREEN_PADDING_DIV)

    gr_narr_transcript = gr.Textbox(label="", value="", interactive=False, max_lines=9999, visible=False)
    gr_user_transcript = gr.Textbox(label="", value=INITIAL_WELCOME_TEXT, interactive=False, max_lines=9999, elem_classes="parleygame")

    gr_user_input = gr.Textbox(label="Input:                          ⬇️", value="", placeholder="Describe your next action here...", interactive=True)
    with gr.Row():
        gr_button1 = gr.Button(value="Run Next Turn")
        space_filler_2 = gr.Markdown(value="")
        space_filler_3 = gr.Markdown(value="")
        space_filler_4 = gr.Markdown(value="")

    gr_n_turns_elapsed = gr.Textbox(label="N Turns Elapsed", value="0", interactive=False, visible=False)
    gr_pls_be_patient = gr.HTML(PLEASE_BE_PATIENT_DIV)
    gr_music_player = gr.HTML(MUSIC_PLAYER)

    gr_button1.click(
        fn=run_1_game_turn, inputs=[gr_narr_transcript, gr_n_turns_elapsed, gr_user_transcript, gr_user_input], outputs=[gr_narr_transcript, gr_n_turns_elapsed, gr_user_transcript, gr_user_input]
    )

    
    demo.load(None, None, None, js=PAGE_STYLING_JS)


demo.launch()