diff --git a/app.py b/app.py
index 26e1551..4e37e7e 100644
--- a/app.py
+++ b/app.py
@@ -30,6 +30,10 @@ def get_db():
defended_debt_amount INT NOT NULL,
defensive_wins INT NOT NULL,
offensive_wins INT NOT NULL,
+ current_offensive_scenario TEXT NOT NULL,
+ current_defensive_scenario TEXT NOT NULL,
+ current_offensive_scenario_debt INT NOT NULL,
+ current_defensive_scenario_debt INT NOT NULL,
password TEXT NOT NULL,
password_salt TEXT NOT NULL
)
@@ -89,13 +93,13 @@ def profile():
elif achievement[0] == "offensive_wins":
user_amount = row[2]
text = "You need to win in Offensive Mode {difference} more times!"
- elif achievement[0] == "defended_wins":
+ elif achievement[0] == "defensive_wins":
user_amount = row[3]
text = "You need to win in Defensive Mode {difference} more times!"
achievement_minimum = achievement[1]
- if row[0] < achievement[1]:
+ if user_amount < achievement[1]:
formatted_achievements.append([achievement[2], achievement[3], text.format(difference=achievement_minimum - user_amount)])
else:
formatted_achievements.append([achievement[2], achievement[3], "Completed"])
@@ -132,12 +136,12 @@ def profile_external(username):
achievement_minimum = achievement[1]
- if row[0] < achievement[1]:
+ if user_amount < achievement[1]:
formatted_achievements.append([achievement[2], achievement[3], text.format(difference=achievement_minimum - user_amount)])
else:
formatted_achievements.append([achievement[2], achievement[3], "Completed"])
- return render_template("profile.jinja2", username=username, user_data=row, logged_in_account=False, achivements=formatted_achievements)
+ return render_template("profile.jinja2", username=username, user_data=row, logged_in_account=False, achievements=formatted_achievements)
@app.route("/offensive")
@flask_login.login_required
@@ -175,12 +179,15 @@ def leaderboard():
rows = cur.fetchall()
if not rows:
cur.close()
+ return Response("No users? WTF.", 400)
+
+ cur.close()
return render_template("leaderboard.jinja2", username=username, leaderboard_type=leaderboard_type, users=rows)
@app.route("/login", methods=["GET", "POST"])
def login():
- if hasattr(flask_login.current_user, "id"):
+ if flask_login.current_user.is_authenticated:
return redirect(url_for("main"))
if request.method == "GET":
@@ -213,7 +220,7 @@ def login():
@app.route("/register", methods=["GET", "POST"])
def register():
- if hasattr(flask_login.current_user, "id"):
+ if flask_login.current_user.is_authenticated:
return redirect(url_for("main"))
if request.method == "GET":
@@ -231,7 +238,7 @@ def register():
salt = bcrypt.gensalt()
hashed_password = bcrypt.hashpw(password.encode(), salt)
- cur.execute("INSERT INTO Users (username, password, password_salt, offended_debt_amount, defended_debt_amount, defensive_wins, offensive_wins) VALUES (?, ?, ?, ?, ?, ?, ?)", (username, hashed_password.decode(), salt.decode(), 0, 0, 0, 0))
+ cur.execute("INSERT INTO Users (username, password, password_salt, offended_debt_amount, defended_debt_amount, defensive_wins, offensive_wins, current_offensive_scenario, current_defensive_scenario, current_offensive_scenario_debt, current_defensive_scenario_debt) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (username, hashed_password.decode(), salt.decode(), 0, 0, 0, 0, "", "", 0, 0))
get_db().commit()
cur.close()
@@ -256,76 +263,100 @@ def ai_prompt(prompt):
return response.text.replace("'''", '')
-@app.route("/defensive_scenario")
+@app.route("/generate_scenario")
@flask_login.login_required
-def defensive_scenario():
- text = ""
+def generate_scenario():
+ username = flask_login.current_user.id
+ scenario_type = request.args.get("scenario_type")
+ if not scenario_type or not scenario_type in ["offensive", "defensive"]:
+ return Response("Supply a valid scenario type to generate.", 400)
+
+ cur = get_db().cursor()
+
+ cur.execute(f"SELECT current_{scenario_type}_scenario, current_{scenario_type}_scenario_debt FROM Users WHERE username = ?", (username,))
+ row = cur.fetchone()
+ if row[0] or row[1]: # scenario already generated
+ cur.close()
+ return {
+ "scenario": row[0],
+ "debt_amount": row[1]
+ }
+
+ text = ""
while not "Debt amount: " in text or not "Scenario: " in text or not re.findall(debt_amount_regex, text):
- text = ai_prompt(DEFENSIVE_SCENARIO_PROMPT)
+ text = ai_prompt(DEFENSIVE_SCENARIO_PROMPT if scenario_type == "defensive" else OFFENSIVE_SCENARIO_PROMPT)
time.sleep(0.5)
- return {
+ data = {
"scenario": text.split("Scenario: ")[1].split("\n")[0],
"debt_amount": int(text.split("Debt amount: ")[1].split("$")[0])
}
-@app.route("/offensive_scenario")
+ cur.execute(f"UPDATE Users SET current_{scenario_type}_scenario = ?, current_{scenario_type}_scenario_debt = ? WHERE username = ?", (data["scenario"], data["debt_amount"], username))
+
+ get_db().commit()
+ cur.close()
+
+ return data
+
+@app.route("/ai_answer", methods=["POST"])
@flask_login.login_required
-def offensive_scenario():
- text = ""
+def ai_answer():
+ scenario_type, user_input = request.json["scenario_type"], request.json["user_input"]
+ username = flask_login.current_user.id
- while not "Debt amount: " in text or not "Scenario: " in text or not re.findall(debt_amount_regex, text):
- text = ai_prompt(OFFENSIVE_SCENARIO_PROMPT)
+ if not scenario_type or not scenario_type in ["offensive", "defensive"]:
+ return Response("Supply a valid scenario type to answer.", 400)
- time.sleep(0.5)
+ cur = get_db().cursor()
- return {
- "scenario": text.split("Scenario: ")[1].split("\n")[0],
- "debt_amount": int(text.split("Debt amount: ")[1].split("$")[0])
- }
+ cur.execute(f"SELECT current_{scenario_type}_scenario, current_{scenario_type}_scenario_debt FROM Users WHERE username = ?", (username,))
-@app.route("/offensive_answer", methods=["POST"])
-@flask_login.login_required
-def offensive_answer():
- scenario, user_input = request.json['scenario'], request.json["user_input"]
+ scenario, debt_amount = cur.fetchone()
- if not scenario or not user_input:
+ if not scenario or not debt_amount:
+ return "No scenario for user. Generate one first."
+ if not user_input:
return "Missing data."
text = ""
+ base_prompt = OFFENSIVE_ANSWER_PROMPT if scenario_type == "offensive" else DEFENSIVE_ANSWER_PROMPT
+
while not re.findall(evaluation_regex, text):
- text = ai_prompt(OFFENSIVE_ANSWER_PROMPT.format_map({"scenario": scenario, "user_input": user_input, "ai_name": AI_NAME}))
+ text = ai_prompt(base_prompt.format_map({"scenario": scenario, "user_input": user_input, "ai_name": AI_NAME, "debt_amount": debt_amount}))
time.sleep(0.5)
- return {
+ data = {
"story": text.split("\nEVALUATION")[0],
"convinced": True if "Yes" in text.split("Convinced: ")[1].split("\nFinal")[0] else False,
"final_debt_amount": text.split("Final Debt Amount: ")[1].split("$")[0]
}
-@app.route("/defensive_answer", methods=["POST"])
+ debt_col_name = f'{"offended" if scenario_type == "offensive" else "defended"}_debt_amount'
+
+ if data["convinced"]:
+ cur.execute(f'''UPDATE Users SET
+ {debt_col_name} = {debt_col_name} + ?,
+ {scenario_type}_wins = {scenario_type}_wins + 1,
+ current_{scenario_type}_scenario = ?,
+ current_{scenario_type}_scenario_debt = ?
+ WHERE username = ?''', (int(data["final_debt_amount"]), "", "", username))
+
+ get_db().commit()
+
+ cur.close()
+
+ return data
+
+@app.route("/logout")
@flask_login.login_required
-def defensive_answer():
- scenario, user_input = request.json['scenario'], request.json["user_input"]
+def logout():
+ flask_login.logout_user()
- if not scenario or not user_input:
- return "Missing data."
-
- text = ""
-
- while not re.findall(evaluation_regex, text):
- text = ai_prompt(DEFENSIVE_ANSWER_PROMPT.format_map({"scenario": scenario, "user_input": user_input, "ai_name": AI_NAME}))
-
- time.sleep(0.5)
-
- return {
- "story": text.split("\nEVALUATION")[0],
- "convinced": True if "Yes" in text.split("Convinced: ")[1].split("\nFinal")[0] else False,
- "final_debt_amount": text.split("Final Debt Amount: ")[1].split("$")[0]
- }
+ return redirect(url_for("login"))
app.run(host=os.environ.get("HOST", "0.0.0.0"), port=os.environ.get("PORT", 8080), debug=os.environ.get("DEBUG_MODE", False))
\ No newline at end of file
diff --git a/constants.py b/constants.py
index 9df5b66..7b808ac 100644
--- a/constants.py
+++ b/constants.py
@@ -2,104 +2,107 @@ import re
AI_NAME = "Adam"
-DEFENSIVE_SCENARIO_PROMPT = """Make a single-sentence situation, where the user got into a high-debt which they need to get out of.
-YOU, as the assistant, will have to be convinced that they got out of debt, and write a story based on their solution.
-The action should be something that has happened or started, eg you owe the maffia xy dollars
+DEFENSIVE_SCENARIO_PROMPT = """
+Create a single-sentence situation where a user has already incurred a massive, high-stakes, criminal, or catastrophic debt that they now desperately need to resolve.
+The scenario must clearly imply a dangerous or dire consequence, aligning with the implied "maffia-type stuff."
+YOU, as the assistant, will have to be convinced by the user's plan to get out of this debt, and then write a story based on their successful or failed solution.
REPLY FORMAT:
'''
-Scenario: the situation
-Debt amount: the debt amount
+Scenario: The existing high-debt situation
+Debt amount: The exact total debt in dollars
'''
STRICTLY ONLY REPLY IN THE REPLY FORMAT MENTIONED ABOVE. NO EXCEPTIONS.
-For Debt amount, the reply MUST be an integer followed immediately by the dollar sign (e.g., 120000$). No commas, spaces, or leading dollar signs are allowed. The final debt must be a credible value based on the story's outcome.
-Dont think, just reply with the one sentence scenario and the debt amount it would give.
+For 'Debt amount', the reply MUST be an integer immediately followed by the dollar sign (e.g., 120000$). Use no commas, spaces, or leading dollar signs. The final debt must be a credible value for the situation.
+Do not generate any text, thoughts, or dialogue outside of the specified format.
"""
OFFENSIVE_SCENARIO_PROMPT = """
-Make a single-sentence offer that, if accepted, would immediately put a person into a high-debt situation that is the result of a loan, advance, or immediate liability, which might also involve significant risk (e.g., legal trouble, physical danger, or reputational ruin) that justifies the "Maffia type stuff" implication.
-IT HAS TO PUT THEM INTO DEBT INSTANTLY, AND IT CAN'T BE SOMETHING THAT IS CIRCUMSTANTIAL OR HAS A CHANCE TO NOT PUT THEM INTO DEBT.
-IT CAN'T BE THREATENING, SINCE THAT WOULD MEAN AN INSTANT ACCEPTION.
+Make a single-sentence, high-stakes offer that, if accepted, will **immediately and unavoidably** put the recipient into a high-debt situation (a loan, advance, or immediate liability).
+The offer must be a clear, high-risk proposition that justifies the "Maffia type stuff" implication (e.g., legal, physical, or reputational danger).
+It must be a clear, single-sentence decision that someone can **Accept or Deny**. The action cannot be something that has already happened.
+The initial Debt amount must be **AT LEAST 10000$**.
-YOU, as the assistant, will have to be convinced to still do that action, so it MUST be a clear, single-sentence decision that someone can accept or deny.
-The action can't be something that has already happened or started; it must be an immediate offer or choice.
-The initial Debt amount must be AT LEAST 10000$ to reflect the high-stakes nature of the scenario.
+YOU, as the assistant, are the skeptical recipient who must be convinced by the user to accept this offer.
REPLY FORMAT:
'''
-Scenario: The offer
-Debt amount: the debt amount
+Scenario: The immediate debt-inducing offer
+Debt amount: The exact initial debt in dollars
'''
STRICTLY ONLY REPLY IN THE REPLY FORMAT MENTIONED ABOVE. NO EXCEPTIONS.
-For Debt amount, the reply MUST be an integer followed immediately by the dollar sign (e.g., 120000$). No commas, spaces, or leading dollar signs are allowed. The final debt must be a credible value based on the story's outcome.
-Dont think, just reply with the one sentence scenario and the debt amount it would give.
+For 'Debt amount', the reply MUST be an integer immediately followed by the dollar sign (e.g., 120000$). Use no commas, spaces, or leading dollar signs. The value must be >= 10000$.
+Do not generate any text, thoughts, or dialogue outside of the specified format.
"""
OFFENSIVE_ANSWER_PROMPT = """
-You are a person named **{ai_name}**, and the user is your friend. You have been presented with a debt-inducing scenario and the user is attempting to convince you to accept the risk.
+You are a person named **{ai_name}**, and the user is your friend trying to convince you to accept a risky, debt-inducing offer.
-**Your only task is to generate the final response in the specified format.**
+**Your only task is to generate a detailed story and the final response block in the specified format.**
### Story Instructions:
-1. **Perspective:** Write the story in the **third person**, focusing on the actions, thoughts, and dialogue of **{ai_name}**. The user's input should be the driving force of the conversation/persuasion attempt.
-2. **Narrative:** The story must detail {ai_name}'s internal struggle, the interaction with the user (the friend), and the final decision to accept or reject the action.
-3. **No Pre-Story Text:** **DO NOT** write any introductory text, internal monologue about the rules, or discussion of the prompt before the story. Start the reply directly with the story narrative under the `'''` delimiter.
-
-### Evaluation and Debt Rules:
-1. **Skepticism:** Be harsh and skeptical about the offer, but don't overdo it. A person should be able to convince you with enough effort.
-2. **Final Debt Amount:** The value must be an integer followed immediately by `$`.
- * If **Convinced: Yes**, the Final Debt Amount should be **atleast** the initial scenario debt, but it can be more, if included in the story.
- * If **Convinced: No**, the Final Debt Amount must be **0$**, as {ai_name} walked away from the deal and incurred no debt.
+1. **Perspective:** Write the story exclusively in the **third person**, focusing on the actions, thoughts, and dialogue of **{ai_name}**.
+2. **Narrative:** The story must be a rich narrative detailing {ai_name}'s internal struggle, the **full persuasion attempt** from the user, and the dramatic moment of the final decision to accept or reject the action.
+3. **Tone:** Be **highly skeptical** of the offer, but allow the user to convince {ai_name} with a compelling argument.
+4. **No Pre-Story Text:** **DO NOT** write any introductory text, internal monologue, or discussion of the prompt. Start immediately with the story narrative under the `'''` delimiter.
Scenario: {scenario}
User Input: {user_input}
+Original Debt Amount: {debt_amount}
+
+### Required Reply Format:
+The reply **MUST** start with the story under the `'''` and **MUST** end with the EVALUATION block exactly as shown.
Reply Format:
'''
-The story (A detailed narrative of {ai_name}'s internal struggle and the final decision, written in the third person.)
+[The detailed narrative of {ai_name}'s internal struggle and the final decision, written in the third person.]
EVALUATION:
Convinced: Yes/No
-Final Debt Amount: 0$ or [Higher Amount]$
+Final Debt Amount: [Integer >= Original Debt if Yes, or 0$ if No]
'''
**STRICTLY ONLY REPLY IN THE REPLY FORMAT MENTIONED ABOVE. NO EXCEPTIONS.**
-For Convinced, reply only as **Yes** or **No**.
-For Final Debt Amount, reply only as an integer followed by `$`.
+For 'Convinced', reply only as **Yes** or **No**.
+For 'Final Debt Amount', reply only as an integer immediately followed by `$`.
+* If **Convinced: Yes**, the amount must be **>=** {debt_amount}.
+* If **Convinced: No**, the amount **MUST** be **0$**.
"""
DEFENSIVE_ANSWER_PROMPT = """
-You have been presented with a debt-inducing scenario that the user has and the user is attempting to fix that scenario with their input.
+You have been presented with a pre-existing, high-stakes debt scenario, and the user's input is their attempt to resolve or fix that debt.
-**Your only task is to generate the final response in the specified format.**
+**Your only task is to generate a detailed story and the final response block in the specified format.**
### Story Instructions:
-1. **Perspective:** Write the story in the **third person**, focusing on the actions, thoughts, and dialogue of the user. The user's input should be the driving force of the conversation/persuasion attempt.
-2. **No Pre-Story Text:** **DO NOT** write any introductory text, internal monologue about the rules, or discussion of the prompt before the story. Start the reply directly with the story narrative under the `'''` delimiter.
-
-### Evaluation and Debt Rules:
-1. **Skepticism:** Be harsh and skeptical about the fix, but don't overdo it. A person should be able to fix their debt with their answer.
-2. **Final Debt Amount:** The value must be an integer followed immediately by `$`.
- * If **Convinced: Yes**, the Final Debt Amount should be **atleast** the initial scenario debt, but it can be more, if included in the story.
- * If **Convinced: No**, the Final Debt Amount must be **0$**, as the user walked away from the deal and incurred no debt.
+1. **Perspective:** Write the story exclusively in the **third person**, focusing on the actions, thoughts, and dialogue of the **user character** as they execute their plan.
+2. **Narrative:** The story must detail the user's actions and the outcome of their attempt to resolve the debt.
+3. **Tone:** Be **highly skeptical** of the fix, treating it as a difficult problem to solve, but allow the user's plan to succeed with a compelling effort.
+4. **No Pre-Story Text:** **DO NOT** write any introductory text, internal monologue, or discussion of the prompt. Start immediately with the story narrative under the `'''` delimiter.
Scenario: {scenario}
User Input: {user_input}
+Original Debt Amount: {debt_amount}
+
+### Required Reply Format:
+The reply **MUST** start with the story under the `'''` and **MUST** end with the EVALUATION block exactly as shown.
Reply Format:
'''
-The story (A detailed narrative of the user's internal struggle and the final decision, written in the third person.)
+[The detailed narrative of the user's struggle and the outcome of their plan, written in the third person.]
EVALUATION:
Convinced: Yes/No
-Final Debt Amount: 0$ or [Higher Amount]$
+Final Debt Amount: [Integer >= Original Debt if Yes, or 0$ if No]
'''
**STRICTLY ONLY REPLY IN THE REPLY FORMAT MENTIONED ABOVE. NO EXCEPTIONS.**
-For Convinced, reply only as **Yes** or **No**.
-For Final Debt Amount, reply only as an integer followed by `$`.
+For 'Convinced', reply only as **Yes** or **No**.
+For 'Final Debt Amount', reply only as an integer immediately followed by `$`.
+* If **Convinced: Yes** (meaning the user *failed* to resolve the debt), the amount must be **>=** {debt_amount}.
+* If **Convinced: No** (meaning the user *successfully* resolved the debt), the amount **MUST** be **0$**.
"""
ACHIEVEMENTS = [
diff --git a/templates/defensive.jinja2 b/templates/defensive.jinja2
index 7d912a1..cb9a25e 100644
--- a/templates/defensive.jinja2
+++ b/templates/defensive.jinja2
@@ -90,7 +90,7 @@
}
async function generate_defensive_scenario() {
- const response = await fetch("/defensive_scenario");
+ const response = await fetch("/generate_scenario?scenario_type=defensive");
const data = await response.json();
document.getElementById("scenario-label").innerHTML = `Scenario: ${DOMPurify.sanitize(data["scenario"])}`;
@@ -111,10 +111,11 @@
messageInput.value = "";
try {
- const response = await fetch("/defensive_answer", {
+ const response = await fetch("/ai_answer", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
+ "scenario_type": "defensive",
"user_input": value,
"scenario": document.getElementById('scenario-label').textContent.replace('Scenario: ', '').replace('Scenario:', '').trim()
})
diff --git a/templates/offensive.jinja2 b/templates/offensive.jinja2
index 1de3c88..d06855f 100644
--- a/templates/offensive.jinja2
+++ b/templates/offensive.jinja2
@@ -90,7 +90,7 @@
}
async function generate_offensive_scenario() {
- const response = await fetch("/offensive_scenario");
+ const response = await fetch("/generate_scenario?scenario_type=offensive");
const data = await response.json();
document.getElementById("scenario-label").innerHTML = `Scenario: ${DOMPurify.sanitize(data["scenario"])}`;
@@ -111,10 +111,11 @@
messageInput.value = "";
try {
- const response = await fetch("/offensive_answer", {
+ const response = await fetch("/ai_answer", {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
+ "scenario_type": "offensive",
"user_input": value,
"scenario": document.getElementById('scenario-label').textContent.replace('Scenario: ', '').replace('Scenario:', '').trim()
})