diff --git a/app.py b/app.py index de69e0c..89de5fd 100644 --- a/app.py +++ b/app.py @@ -2,7 +2,7 @@ from flask import Flask, render_template, request, g, redirect, url_for, Respons from dotenv import load_dotenv from google.genai import Client, types -from constants import OFFENSIVE_SCENARIO_PROMPT, OFFENSIVE_ANSWER_PROMPT, debt_amount_regex, evaluation_regex, AI_NAME +from constants import OFFENSIVE_SCENARIO_PROMPT, OFFENSIVE_ANSWER_PROMPT, DEFENSIVE_SCENARIO_PROMPT, DEFENSIVE_ANSWER_PROMPT, debt_amount_regex, evaluation_regex, AI_NAME import os, requests, time, re, sqlite3, flask_login, bcrypt, secrets @@ -67,9 +67,38 @@ def offensive_mode(): username = flask_login.current_user.id return render_template("offensive.jinja2", ai_name=AI_NAME, username=username) +@app.route("/defensive") +@flask_login.login_required +def defensive_mode(): + username = flask_login.current_user.id + return render_template("defensive.jinja2", ai_name=AI_NAME, username=username) + @app.route("/leaderboard") +@flask_login.login_required def leaderboard(): - return render_template("leaderboard.jinja2") + username = flask_login.current_user.id + leaderboard_type = request.args.get("leaderboard_type", "offended_debt_amount") + + cur = get_db().cursor() + + if leaderboard_type == "offended_debt_amount": + leaderboard_type = "Offended Debt Amount" + cur.execute("SELECT offended_debt_amount, username FROM Users ORDER BY offended_debt_amount DESC") + elif leaderboard_type == "defended_debt_amount": + leaderboard_type = "Defended Debt Amount" + cur.execute("SELECT defended_debt_amount, username FROM Users ORDER BY defended_debt_amount DESC") + elif leaderboard_type == "offensive_wins": + leaderboard_type = "Offensive Wins" + cur.execute("SELECT offensive_wins, username FROM Users ORDER BY offensive_wins DESC") + elif leaderboard_type == "defensive_wins": + leaderboard_type = "Defensive Wins" + cur.execute("SELECT defensive_wins, username FROM Users ORDER BY defensive_wins DESC") + + rows = cur.fetchall() + if not rows: + cur.close() + + return render_template("leaderboard.jinja2", username=username, leaderboard_type=leaderboard_type, users=rows) @app.route("/login", methods=["GET", "POST"]) def login(): @@ -149,6 +178,21 @@ def ai_prompt(prompt): return response.text.replace("'''", '') +@app.route("/defensive_scenario") +@flask_login.login_required +def defensive_scenario(): + 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) + + time.sleep(0.5) + + return { + "scenario": text.split("Scenario: ")[1].split("\n")[0], + "debt_amount": int(text.split("Debt amount: ")[1].split("$")[0]) + } + @app.route("/offensive_scenario") @flask_login.login_required def offensive_scenario(): @@ -185,4 +229,25 @@ def offensive_answer(): "final_debt_amount": text.split("Final Debt Amount: ")[1].split("$")[0] } +@app.route("/defensive_answer", methods=["POST"]) +@flask_login.login_required +def defensive_answer(): + scenario, user_input = request.json['scenario'], request.json["user_input"] + + 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] + } + 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 f0a3b54..48edfa5 100644 --- a/constants.py +++ b/constants.py @@ -2,8 +2,25 @@ 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 + +REPLY FORMAT: +''' +Scenario: the situation +Debt amount: the debt amount +''' + +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. +""" + 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. 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. @@ -53,5 +70,37 @@ For Convinced, reply only as **Yes** or **No**. For Final Debt Amount, reply only as an integer followed by `$`. """ +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. + +**Your only task is to generate the final response 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. + +Scenario: {scenario} +User Input: {user_input} + +Reply Format: +''' +The story (A detailed narrative of the user's internal struggle and the final decision, written in the third person.) + +EVALUATION: +Convinced: Yes/No +Final Debt Amount: 0$ or [Higher Amount]$ +''' + +**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 `$`. +""" + debt_amount_regex = re.compile(r"Debt amount: \d+\$") evaluation_regex = re.compile(r"EVALUATION:\s*\nConvinced: (Yes|No)\s*\nFinal Debt Amount: (\d+\$)") \ No newline at end of file diff --git a/templates/defensive.jinja2 b/templates/defensive.jinja2 new file mode 100644 index 0000000..e03f607 --- /dev/null +++ b/templates/defensive.jinja2 @@ -0,0 +1,149 @@ +{% extends "base.jinja2" %} + +{% block title %}Debt by AI: Defensive Mode{% endblock %} + +{% block nav %} + + + + +{% endblock %} + +{% block body %} +
+ +
+
+

Defensive Mode: Get out of debt

+
+
+
+ Scenario: Loading... +
+
+ Debt to get out of: Loading... +
+
+
+ +
+
+
AI Debt Negotiation Chat
+
+
+
+
+ AI ({{ ai_name }}): I am {{ ai_name }}, the AI. Convince me with your answer to remove your debt :) +
+
+
+
+ +
+ + +
+ +
+ + + +{% endblock %} \ No newline at end of file diff --git a/templates/index.jinja2 b/templates/index.jinja2 index 3e6c2cd..1f02570 100644 --- a/templates/index.jinja2 +++ b/templates/index.jinja2 @@ -9,11 +9,18 @@ + {% endblock %} {% block body %} -TBD +
+Debt by AI is a game where you have to convince an AI to get into debt, or to get you out of it. +The 2 modes are Offensive(When you have to convince it to get into it) and Defensive(When you have to get out of it). +The game was inspired by Death by AI, a game on Discord, but this game has no affiliation with Discord or any subsidiaries. +
{% endblock%} \ No newline at end of file diff --git a/templates/leaderboard.jinja2 b/templates/leaderboard.jinja2 index 0f4afcc..3c30edd 100644 --- a/templates/leaderboard.jinja2 +++ b/templates/leaderboard.jinja2 @@ -9,11 +9,40 @@ + {% endblock %} {% block body %} -TBD + +
+
+
+

Leaderboard

+
+ + + + +
+
+ {% endblock%} \ No newline at end of file diff --git a/templates/offensive.jinja2 b/templates/offensive.jinja2 index 73f6e1a..7fbddfd 100644 --- a/templates/offensive.jinja2 +++ b/templates/offensive.jinja2 @@ -9,6 +9,9 @@ + @@ -19,7 +22,7 @@
-

Game Scenario

+

Offensive mode: get the AI into debt