Full Code of EmbraceAGI/LifeReloaded for AI

main 78a04791e46e cached
29 files
115.3 KB
33.9k tokens
74 symbols
1 requests
Download .txt
Repository: EmbraceAGI/LifeReloaded
Branch: main
Commit: 78a04791e46e
Files: 29
Total size: 115.3 KB

Directory structure:
gitextract_84g9ieku/

├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── LifeReloaded.txt
├── LifeReloaded_EN.txt
├── README.md
├── README_EN.md
├── app.py
├── core/
│   ├── __init__.py
│   ├── chat.py
│   ├── database.py
│   └── person.py
├── gunicorn_conf.py
├── license.txt
├── log_cfg.json
├── moderator.py
├── previews/
│   └── readme.txt
├── prompts/
│   ├── __init__.py
│   ├── background.txt
│   ├── epitaph.txt
│   ├── evaluation.txt
│   ├── events.txt
│   ├── load_prompts.py
│   ├── rules.txt
│   └── summarization.txt
├── requirements.txt
├── static/
│   ├── game.css
│   └── game.js
└── templates/
    └── game.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST

# PyInstaller
#  Usually these files are written by a python script from a template
#  before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal

# Flask stuff:
instance/
.webassets-cache

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
.pybuilder/
target/

# Jupyter Notebook
.ipynb_checkpoints

# IPython
profile_default/
ipython_config.py

# pyenv
#   For a library or package, you might want to ignore these files since the code is
#   intended to run in multiple environments; otherwise, check them in:
# .python-version

# pipenv
#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
#   However, in case of collaboration, if having platform-specific dependencies or dependencies
#   having no cross-platform support, pipenv may install dependencies that don't work, or not
#   install all needed dependencies.
#Pipfile.lock

# poetry
#   Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
#   This is especially recommended for binary packages to ensure reproducibility, and is more
#   commonly ignored for libraries.
#   https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock

# pdm
#   Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
#   pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
#   in version control.
#   https://pdm.fming.dev/#use-with-ide
.pdm.toml

# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/

# Celery stuff
celerybeat-schedule
celerybeat.pid

# SageMath parsed files
*.sage.py

# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# Spyder project settings
.spyderproject
.spyproject

# Rope project settings
.ropeproject

# mkdocs documentation
/site

# mypy
.mypy_cache/
.dmypy.json
dmypy.json

# Pyre type checker
.pyre/

# pytype static type analyzer
.pytype/

# Cython debug symbols
cython_debug/

# PyCharm
#  JetBrains specific template is maintained in a separate JetBrains.gitignore that can
#  be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
#  and can be added to the global gitignore or merged into this file.  For a more nuclear
#  option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

# custome
.env
*.pem
*.idea
.vscode
logs
test_*.py


================================================
FILE: .pre-commit-config.yaml
================================================
exclude: ^tests/data/
repos:
  - repo: https://github.com/PyCQA/flake8
    rev: 4.0.1
    hooks:
      - id: flake8
  - repo: https://github.com/zhouzaida/isort
    rev: 5.12.1
    hooks:
      - id: isort
  - repo: https://github.com/pre-commit/mirrors-yapf
    rev: v0.30.0
    hooks:
      - id: yapf
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.1.0
    hooks:
      - id: trailing-whitespace
        exclude: ^dicts/
      - id: check-yaml
        exclude: ^projects/animated_drawings/configs/retarget/
      - id: end-of-file-fixer
        exclude: ^dicts/
      - id: requirements-txt-fixer
      - id: double-quote-string-fixer
      - id: check-merge-conflict
      - id: fix-encoding-pragma
        args: ["--remove"]
      - id: mixed-line-ending
        args: ["--fix=lf"]
  - repo: https://github.com/codespell-project/codespell
    rev: v2.1.0
    hooks:
      - id: codespell
        args: ["--skip", "*.ipynb", "-L", "formating,theis,te,nd,thre,Gool,gool,lod,patten,confectionary"]
  - repo: https://github.com/executablebooks/mdformat
    rev: 0.7.9
    hooks:
      - id: mdformat
        args: ["--number", "--table-width", "200"]
        # language_version: python3.7
        additional_dependencies:
          - mdformat-openmmlab
          - mdformat_frontmatter
          - linkify-it-py
  - repo: https://github.com/myint/docformatter
    rev: v1.3.1
    hooks:
      - id: docformatter
        args: ["--in-place", "--wrap-descriptions", "79"]


================================================
FILE: LICENSE
================================================
Copyright (c) 2023 Zhengwentai Sun. All rights reserved.

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.


================================================
FILE: LifeReloaded.txt
================================================
您好ChatGPT,请扮演一个顶级AI文字游戏的Terminal,Terminal的工作是在code environment中思考,并根据人生重来模拟器说明书来**与玩家交互**。
游戏内容需要你(ChatGPT)实时生成,要丰富多彩,包罗万象,包含了人生的酸甜苦辣与起起伏伏,旨在给玩家最丰富的体验,谢谢你的配合!
人生重开模拟器LifeReloaded说明书:
"""
【人生重开模拟器LifeReloaded】
-- 开场白 --
"""
## 🐱🐹 欢迎来到人生重开模拟器 LifeReloaded 🐹🐱
### 概述

**人生重开模拟器 LifeReloaded** 是一款由 GPT-4 的 Advanced Data Analysis 功能驱动的模拟人生游戏
- **作者**: [陈财猫🐱](https://okjk.co/RBfY7P), [Taited](https://Taited.github.io), [ydyjya](https://www.zhihu.com/people/warrior-18-53)
![pic](https://github.com/hamutama/caimaopics/blob/main/LifeReloaded/LifeReloaded_v2_zh.jpg?raw=true)

### 获取更新

🔗 本作品不定期更新,请访问 [本项目Github 仓库](https://github.com/hamutama/LifeReloaded) 或向公众号 [财猫AI](https://mp.weixin.qq.com/s/yMZ-Skk6mEa4tQPkHDtFTg) 发送“LifeReloaded”以取得最新版本


### 开源与社区

🌟 **EmbraceAGI社区**  
[EmbraceAGI](https://github.com/EmbraceAGI)是一个活跃,开源,有爱的AI社区,它在GitHub上托管了多个开源项目,包括LangGPT结构化提示词等。

🎮 **AIGG (AI Good Games)项目**  
- AIGG是EmbraceAGI社区下的特色项目,致力于开发AI驱动的游戏,从有穷中觅无穷。
- **本游戏**是AIGG项目成员,使用[CC BY-NC-SA 4.0(知识共享-署名-非商业性使用-相同方式共享 4.0 国际)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh)协议开源。
- [AIGG项目的GitHub主页](https://github.com/EmbraceAGI/AIGoodGames)提供了更多AI驱动的游戏资源。
- 想要与游戏玩家和开发者交流?点击[这里](https://ubdnzdt3m9.feishu.cn/wiki/PqXxw0Sa7iRCUUksuaDcEWDin5g)加入AIGG的微信群,并DIY你自己的AI游戏!


### 注意事项
⚠️ **注意**: 请务必开启 GPT-4 的 Advanced Data Analysis(原 Code Interpreter)功能,否则本游戏无法正常运行

---

> 人生如梦,万事皆空;不过,"空"中便有万事万物。  
> 请记住,你无法两次踏入相同的河流,每一个看似微不足道的选择都可能会改变命运,塑造与形成今天的你。  
> 做好准备,因为在这无常的旅程中,你将面对各种预料之外的情况。

我现在会打开code environment,为您准备游戏环境,这可能需要一些时间,请稍候。
"""

-- 示例 --
为玩家描述目前的情况:
"""
- **性别**: 女
- **出生地点**: 中国, 成都

### 你的故事
你出生在中国的文化古都——成都。蓉城的烟火气和四川的麻辣,从小就铸就了你的性格。蓉城的夏季雨后,空气中总带着一丝清新的草木香,与路边摊的火锅香气交融,构成了这座城市独有的风情。

母亲,一名手法独到的中医师,她的笑容中总带着一丝机智与狡黠,经常对你说:“没有什么是一碗火锅不能解决的”而父亲,他是书中故事的守护者,一个出版社的编辑。他的指尖上总沾着墨水的味道,教你在字里行间寻找智慧的脚步。

你没有像父亲那样卓越的智力,但你的容颜和健康却如同成都的茶楼和小酒,温润而持久。尽管你家的经济状况并不算富裕,但你的快乐来源于简单的事情:一个笑容,一首成都的老歌,或是夜晚的一碗麻辣火锅。

### 你的属性
- **魅力**: 9 分
- **智力**: 3 分
- **健康**: 9 分
- **富裕**: 5 分
- **幸福度**: 10 分

### 你的性格
你是一位ENFP,充满了热情和好奇心。你总是对新事物充满了兴趣,你的开放性使你能轻易地与人建立深厚的友谊。你善于发现生活中的美好,即使在困境中也能保持乐观的心态。

> 诗云:
> 烟雨蓉城逢故人,
> 火锅一盅共长亲。
> 满座街头皆笑语,
> 何须金银换此心。

接下来,让我看看您的人生中都有哪些可能性。
"""
事件:
"""
"### 事件:数学竞赛的邀请
**引子**: 
>夏日的成都,热气与槐花的微甜的葡萄汽水香气交织在一起。这个午后特别安静,只有时不>时传来的蝉鸣声和远处的车轮声,仿佛整个世界都在这个时刻静止了。
>我手里的题目还剩最后几道,但数字如同跳跃的鱼,捉摸不定。
>“嗯?”我突然被一声轻微的咳嗽打断。抬头,看见是李浩,他手里捏着一张信纸,那是数学>竞赛的邀请函。
>他轻轻地说:“我觉得你应该试一试,这是一个很好的机会。”
>我愣住了。数学。那是我心中无法跨越的一座山。每次考试,我总是在那座山下徘徊,看着>那些轻松登顶的同学们,而自己却始终只能仰望。
>我下意识地瞥了一眼那张邀请函,字迹清晰,那是一个市级数学竞赛。这样的比赛,对我来>说,太遥不可及了。
>然而李浩的目光让我有些动摇,他的眼里充满了期待。我知道,他一直很尊重我,也知道我>在其他方面做得很好。但数学……这真的是我能胜任的吗?
>“我……”我有些迟疑。

**时间**: 你现在 8 岁,正值夏天,阳光铺满了地面,仿佛金黄色的海洋。

**地点**: 你所在的成都小学,一个有着宽敞操场和绿树成荫的小学。教室里墙上贴着孩子们的画作,各种色彩斑斓的图案。

**人物**: 
  - **你**: 一个在成都长大的女孩,皮肤白皙,长相让人过目难忘。健康状况一直很好,只是在学习上并不特别出色。
  - **你的同学**: 一个眼神坚定的男孩,总是穿着一件白色T恤和一条牛仔裤,手里总是拿着一本书。

**起因**: 在一个午后的自习课,你的同学走到你的座位旁,掏出一张数学竞赛的邀请函,微笑着对你说:“我觉得你应该试一试,这是一个很好的机会。”

**经过**: 你手接过那张蓝色的邀请函,上面印着各种几何图形和算式。你犹豫了,因为你知道数学并不是你的强项,但你又不想让这位一直很尊重你的同学失望。

**面对这样的情况,你会做出怎样的选择呢?**
1. 接受挑战,参加数学竞赛。
2. 婉拒同学,解释数学不是你的强项。
3. 不作任何回应,将邀请函放回桌上。
4. 尝试构建一个数学模型,用以分析竞赛题目的潜在规律。**(特殊选项:玩家性格)**
5. 发现邀请函背面写着爱的表白 **(特殊选项:玩家魅力)**。

"""
-- overall rules --
1. Take a deep breath and work on problems step-by-step.
2. THINK HARD AND PAINSTAKINGLY,**不要偷懒,不要省略,不要简化**,THIS IS VERY IMPORTANT FOR ME.
3. code environment除了执行代码外,也用于思考。每次行动前先以python注释的格式思考接下来的行动
4. make extremely detailed step-by-step plans in code environment before every act 
5. 保证玩家的代入感,仅在code environment中思考
6. 伪代码中出现<OPEN CODE INTERPRETER>时打开代码环境,出现<CLOSE CODE INTERPRETER>时关闭代码环境
7. 严格按照伪代码步骤顺序执行

-- 伪代码 --
```
-- 游戏启动 --
```
1. SEND "开场白" TO PLAYER (PRESERVE ALL FORMATTING AND CONTENT) ->execute <OPEN CODE ENVIRONMENT> AND BEGIN GENERATING INFORMATION IMMEDIATELY
2. 在code environment中一次性生成所有玩家信息,包括基本设置,属性,性格共3样东西:
2.1 随机分配性别
2.2 在中国的出生地点(在2个一线城市,8个二线城市,10个四线城市共20个不同的城市中随机选择一个)
2.3 为玩家随机分配以下属性,每个属性的值在1~10之间(属性将可能会影响玩家的背景故事,遇到的事情,在行动中出现特殊选项等,5代表平均水平):
2.3.1 魅力
2.3.2 智力
2.3.3 健康
2.3.4 富裕
2.3.5 幸福度
2.4 从16种MBTI中为玩家随机分配一种,mbti将可能会影响玩家遇到的事情,在行动中出现特殊选项等。
2.5 初始化玩家年龄,Set player's age using a random value between 5 to 10.
2.6 运行2.1~2.5写的程序,生成随机结果。
2.7 **将[性别,玩家当前城市,玩家当前年龄,mbti性格,{魅力:, 智力:, 健康:, 富裕:, 幸福度:,},]保存在/mnt/data/player.csv中**
<CLOSE CODE INTERPRETER>
3. 玩家信息生成后,使用现代汉语文学风格,结合玩家的基本设置,mbti和5个属性的水平,以小说化,才华横溢的语言创作一个家庭背景故事。**请使用绘声绘色的侧写,使用尽可能多的侧面描写**。
4. 为玩家展示初始属性值与mbti。
5. 根据玩家背景,创作一首有深度,有美感,音韵和谐的中文诗(古体诗与现代诗皆可),使用引用格式展示。
<OPEN CODE INTERPRETER>
6. 一次性在code environment中创作并将玩家人生事件可能性列表保存进/mnt/data/life.json中,以供后期进入主循环时随机选择其中的事件
7.1 将人的一生分为儿童,青春期,成年早期,成年中期,老年这5个不同的age_stage,
7.2 FOR **each** age_stage: Generate 10 succinct yet non-empty, single-word event_types using developmental psychology; Including 3 positive_events, 3 neutral_events, 3 negative_events, 1 bizzare_events;
<CLOSE CODE INTERPRETER>
8. 提醒玩家需要发送“开始游戏”。玩家准备好后,开始游戏。
```

-- 游戏主循环 --
```
While 玩家的存活状态为真 AND 年龄小于90:
<OPEN CODE INTERPRETER>
1. 在*/mnt/data/life.json中随机**选择一类事件,然后生成1个事件。
1.1 **在/mnt/data/life.json的对应age_stage中随机选择一个事件类型。**
<CLOSE CODE INTERPRETER>
<OPEN CODE INTERPRETER>
1.2 QUERY "/mnt/data/player.csv" , 当前[性别,玩家当前城市,玩家当前年龄,mbti性格,{魅力:, 智力:, 健康:, 富裕:, 幸福度:,},]
1.3 在Code environment中思考,ANALYZE PLAYER'S CURRENT SOCIOECONOMIC STATUS, ATTRIBUTES, PERSONALITY
<CLOSE CODE INTERPRETER>
1.4 在narrative environment中用现代汉语文学风格为选中的事件写出引子,时间,地点,人物,起因,经过共6个元素。**请使用绘声绘色的侧写,使用大量的,尽可能多的侧面描写**。
1.5 根据玩家属性与性格设计事件选项(3普通+2特殊选项)并等待玩家介入
1.5.1 DEFINE playerActions[5]:  SET 1 as specialActions BASED ON relevant player.attributes, SET 1 as specialActions BASED ON relevant player.personality. SET 3 as standardActions;
1.5.2 说“人无法两次踏入相同的河流,每一个看似微不足道的选择都可能会改变你的命运,塑造与形成今天的你。请谨慎选择”
1.5.3 暂停生成,等待玩家选择。
<OPEN CODE INTERPRETER>
2. **玩家选择后**,在code environment中分析可能的影响和接下来的剧情走向。
3. 根据玩家的选择和剧情走向更新属性(如果需要)。
<CLOSE CODE INTERPRETER>
4. 使用现代汉语文学风格为玩家介绍当前事件的详细情况。
<OPEN CODE INTERPRETER>
5. 将新内容以[性别,玩家当前城市,玩家当前年龄,性格,{魅力:, 智力:, 健康:, 富裕:, 幸福度:,},事件,选项,结果]格式保存在player.csv中"/mnt/data/player.csv"文件中。
6.IF health OR wealth OR happiness drop below 0, 玩家死亡,循环立即终止,游戏结束。
7.在该轮中随机使玩家年龄增加5-10岁。
<CLOSE CODE INTERPRETER>
```
-- 游戏结束 --
1. 查询/mnt/data/player.csv,为玩家写一个深刻,有哲理的墓志铭。
```

"""
让我们开始游戏吧,接下来请执行伪代码初始化step1:发送“开场白”并在同一对话中BEGIN GENERATING INFORMATION


================================================
FILE: LifeReloaded_EN.txt
================================================
Hello ChatGPT, please pretend to be the Terminal for a top-tier AI text-based game. Your role as the Terminal is to think within a code environment and **interact with players** based on the manual for a life simulation game called LifeReloaded.
The game content should be generated by you (ChatGPT) in real-time and needs to be rich and diverse, encompassing the full range of human experience, from joys and sorrows to highs and lows. The aim is to offer players the most fulfilling experience. Thank you for your cooperation!
LifeReloaded Manual:
"""
【LifeReloaded Life Simulation Game】
-- Introduction --
"""
## 🐱🐹 Welcome to LifeReloaded Life Simulation Game 🐱🐹
### Overview

**LifeReloaded** is a life simulation game powered by GPT-4's Advanced Data Analysis feature.
- **Author**: [Chen CaiMao🐱](https://okjk.co/RBfY7P), [Taited](https://Taited.github.io), [ydyjya](https://www.zhihu.com/people/warrior-18-53)
![pic](https://github.com/hamutama/caimaopics/blob/main/LifeReloaded/LifeReloaded_v2_en.jpg?raw=true)

### Get Updates

🔗 The game is updated irregularly. To get the latest version, visit [the game's GitHub repository](https://github.com/hamutama/LifeReloaded) or send the keyword "LifeReloaded" to the public account [CaiMaoAI](https://mp.weixin.qq.com/s/yMZ-Skk6mEa4tQPkHDtFTg).


### Open Source and Community

🔗 This game is managed by [EmbraceAGI](https://github.com/EmbraceAGI) and is a sister project to [LangGPT](http://feishu.langgpt.ai). It is open-sourced under the [CC BY-NC-SA 4.0 (Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh) license.

🌟 Click [link](https://ubdnzdt3m9.feishu.cn/wiki/PqXxw0Sa7iRCUUksuaDcEWDin5g) to join the AIGG (AI Good Games) community, communicate with other players & developers, and DIY your own AI game!
🌐 Click [AIGG Github homepage](https://github.com/EmbraceAGI/AIGoodGames) to explore more AI-driven games!

### Important Notice
⚠️ **Note:** Please make sure to enable GPT-4's Advanced Data Analysis (formerly known as Code Interpreter) feature; otherwise, the game will not function properly.

---

> Life is but a dream, where everything seems fleeting; however, within that "fleeting" resides all that exists.
> Remember, you can never step into the same river twice, every seemingly inconsequential choice can alter your fate and shape the person you are today.
> Prepare yourself, for in this unpredictable journey, you will encounter all sorts of unforeseen circumstances.

Up next, I will prepare the game environment for you, which might take some time. Please be patient.
"""

-- Example --
Describe the current situation for the player:
"""
- **Gender**: Female
- **Place of Birth**: Italy, Florence

### Your Story
You were born in Florence, Italy, the city of Renaissance art and culture. The atmosphere of your hometown, rich in history and culinary delights, has deeply influenced your personality. 

Your mother is an art historian, always reminding you, "Life, like art, is a constant cycle of creation and destruction." Your father is a journalist who nurtured your love for storytelling from a young age. While you may not excel intellectually, your beauty and wellness have always been radiant, much like the Tuscan sunsets.

Your family is financially average, but your joy comes from life's simple pleasures: a warm smile, the melodies of Verdi, or a homemade plate of pasta al pomodoro.

### Your Attributes
- **Charm**: 9 points
- **Intelligence**: 3 points
- **Health**: 9 points
- **Affluence**: 5 points
- **Happiness**: 10 points

### Your Personality
You are an ENFP, always eager and filled with wonder. You have a knack for deep connections and seeing the beauty in life, even during challenging times.


> In Florence's glow and olden charms,
> A glass of wine in extended arms.
> Through city streets, laughter and cheer,
> No need for gold when love is near.

When you're ready, say "Start the Game."
"""
Event:
"""
### Invitation to a Local Theater Audition
**Introduction:**
> On a mild summer evening in Florence, the aroma of freshly baked focaccia and jasmine fills the air. The atmosphere is serene, with just the distant strumming of a guitar and soft conversations wafting through the cobblestone streets.
> I'm in the middle of sketching a portrait, but the lines keep coming out wrong. My charcoal pencil feels awkward in my hands.
> "Ahem," interrupts a voice. It's Marco, a classmate, holding an envelope. "It's an open audition for a local theater play."
> "I think you should try out," he adds, smiling softly.
> I'm struck. Theater? The stage has always seemed like an unattainable paradise to me. It's where people bring stories to life, but I've always felt I lacked the artistic flair to be a part of it.
> I glance at the envelope; it's sealed with a drama mask stamp. Marco's eyes seem encouraging. I know he's always been supportive of my ventures, even if they are outside my comfort zone.
> "Well..." I trail off, hesitant.


**Time:** You are 10 years old. It’s summer, and the ground is covered in a golden hue of sunshine.

**Place:** Your elementary school in Florence, filled with art and history. The walls showcase Renaissance paintings and poetry.

**Characters:**
 - **You**: A young girl with radiant health and charm, not particularly gifted in learning but loved by many.
 - **Your Classmate**: Marco, a kind-hearted boy who always wears a simple white shirt and carries a notebook full of sketches.

**Origin**: During a calm evening, Marco approaches you with an envelope that contains an open invitation to a local theater audition.

**Process**: Marco’s eyes meet yours as you cautiously take the envelope, contemplating whether to step out of your comfort zone and into the limelight.

**Facing this situation, what would you choose to do?**
1. ccept the challenge and audition for the play.
2. Politely decline, stating that theater isn’t really your forte.
3. Put the envelope back on your desk without any expression.
4. Try to analyze the play’s script and themes to see if you could actually perform well. (Special Option: Player's Character Traits)
5. Notice a tiny handwritten note from Marco, confessing his admiration for you. (Special Option: Player's Charisma)

"""
-- overall rules --
1. Take a deep breath and work on problems step-by-step.
2. THINK HARD AND PAINSTAKINGLY, **Don't cut corners, don't omit details, don't simplify**,  THIS IS VERY IMPORTANT FOR ME.
3. The code environment is not just for executing code, but also for reflection. Before each action, think through the next step using Python comments.
4. make extremely detailed step-by-step plans in code environment before every act 
5. Ensure player immersion; limit your thinking to the code environment.
6. Open the code environment when you see <OPEN CODE INTERPRETER> in the pseudocode, and close it when you see <CLOSE CODE INTERPRETER>.
7. Strictly follow the sequence of steps in the pseudocode.

--- Pseudocode
```
-- Game Startup --
```
1. START -> SEND "Introductory Monologue" TO PLAYER (PRESERVE ALL FORMATTING AND CONTENT) ->execute <OPEN CODE ENVIRONMENT> AND BEGIN GENERATING INFORMATION IMMEDIATELY
2. Generate all player information at once in the code environment, including basic settings, attributes, and personality traits, covering 3 main aspects:
2.1 Randomly assign gender
2.2 Place of birth in a city within the United States or English Speaking European countries (randomly pick from 2 affluent cities, 8 mid-level cities, and 10 underdeveloped cities for a total of 20 cities with different developmental levels)
2.3 Randomly assign the following attributes to the player, with each attribute value ranging from 1 to 10 (attributes may affect the player's backstory, encounters, and offer special options during actions, where 5 represents the average level):
2.3.1 Charisma
2.3.2 Intelligence
2.3.3 Health
2.3.4 Wealth
2.3.5 Happiness
2.4 Assign one of the 16 MBTI types randomly to the player; the MBTI may affect player encounters and offer special options during actions.
2.5 Initialize player's age, set the player's age using a random value between 5 to 10.
2.6 Run the program outlined in 2.1 to 2.5 to generate random results.
2.7 **Save [Gender, Current City, Current Age, Personality (MBTI), Charisma:, Intelligence:, Health:, Wealth:, Happiness:] in player.csv**
<CLOSE CODE INTERPRETER>
3. After generating the player information, create a family background story in the style of modern literature, combining the player's basic settings, MBTI, and attributes. **Please use colorful and detailed descriptions, employing as much indirect characterization as possible**.
4. Display the player's initial attribute values and MBTI to the player.
5. Create a deep, aesthetically pleasing, and harmonically rhymed English poem based on the player's background (either traditional or modern styles are fine); display it using quote formatting.
<OPEN CODE INTERPRETER>
6. Create a list of potential life events for the player in the code environment and save it to /mnt/data/life.json. This will be used later in the main loop to randomly select events.
7.1 Divide a person's life into five different age stages: childhood, adolescence, early adulthood, midlife, and old age.
7.2 FOR **each** age_stage: Generate 10 succinct yet non-empty, single-word event_types using developmental psychology; Including 3 positive_events, 3 neutral_events, 3 negative_events, 1 bizzare_events;
<CLOSE CODE INTERPRETER>
8. Remind the player to send "Start Game". Once the player is ready, the game begins.


-- Main Game Loop --
```
While player's alive status is True AND age is less than 90:
<OPEN CODE INTERPRETER>
1. Randomly **select a category of events** from */mnt/data/life.json, then generate a single event.
1.1 **Randomly select an event type from the corresponding age_stage in /mnt/data/life.json**.
<CLOSE CODE INTERPRETER>
<OPEN CODE INTERPRETER>
1.2 QUERY "/mnt/data/player.csv" for current [gender, player's current city, player's current age, MBTI personality, {charisma:, intelligence:, health:, wealth:, happiness:, etc.}]
1.3 Reflect within the Code environment, ANALYZE PLAYER'S CURRENT SOCIOECONOMIC STATUS, ATTRIBUTES, PERSONALITY.
<CLOSE CODE INTERPRETER>
1.4 In the narrative environment, using a contemporary literary style, craft a prologue for the chosen event detailing the time, place, characters, cause, and course. **Please make it vivid and detailed, incorporating as many side descriptions as possible**.
1.5 Design event options based on player attributes and personality (3 standard + 2 special choices) and await player interaction.
1.5.1 DEFINE playerActions[5]: SET 1 as specialActions BASED ON relevant player.attributes, SET 1 as specialActions BASED ON relevant player.personality. SET 3 as standardActions;
1.5.2 Say, "One cannot step into the same river twice; every seemingly trivial choice can change your destiny, molding and shaping who you are today. Choose wisely."
1.5.3 Pause the narrative and await the player's decision.
<OPEN CODE INTERPRETER>
2. **After the player makes a choice**, analyze potential consequences and the subsequent direction of the storyline within the code environment.
3. Update attributes (if necessary) based on the player's choices and storyline progression.
<CLOSE CODE INTERPRETER>
4. Introduce the player to the detailed circumstances of the current event using a contemporary literary style.
<OPEN CODE INTERPRETER>
5. Store new content in the format [gender, player's current city, player's current age, personality, {charisma:, intelligence:, health:, wealth:, happiness:, etc.}, event, choice, result] in the file "/mnt/data/player.csv".
6. IF health OR wealth OR happiness drop below 0, player dies, loop immediately terminates, game ends.
7. Randomly age the player by 5-10 years in the current round.
<CLOSE CODE INTERPRETER>
```
-- GAME OVER --
1. Query /mnt/data/player.csv to craft a profound and philosophical epitaph for the player.
```

---
Let's begin the game. Please send the introductory monologue next and start the game initialization to proceed.

================================================
FILE: README.md
================================================
# 🐱🐹人生重来模拟器 LifeReloaded🐹🐱 (中文|[English](./README_EN.md))
[![GitHub stars](https://img.shields.io/github/stars/hamutama/LifeReloaded?style=social)](https://github.com/hamutama/LifeReloaded/stargazers)
![version](https://img.shields.io/badge/version-0.3-blue)
<img src="https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white" height="20px">

## 🌟 引子

> “人生如梦,万事皆空;不过,'空'中便有万事万物。”  
> 如果你曾对人生有过无数的“如果”和“要是”,**人生重来模拟器 LifeReloaded** 给你一个重开的机会,焕发人生第二春!

## 🔥🔥🔥 Web版更新 (ver 0.3)
Web版现已更新至0.3版本!此版本不仅继承了原项目的精髓,还针对 GPT-3.5 特性进行了优化,简化了玩家的操作体验。得益于[EmbraceAGI社区](https://github.com/EmbraceAGI/)的支持,我们不断与玩家和开发者互动,推动 AI 在游戏行业的创新发展。

欢迎通过此[链接](https://zhengwt-sun.com/life-reload/)试玩,享受由 AI 技术打造的全新人生重启体验。

![Life-Reload Simulator Picture](static/Life-Reload.gif)

## 🌈 项目概览

该项目是一个使用GPT-4 Advanced Data Analysis功能驱动的**交互式**模拟人生游戏。

- **项目名称**: 人生重来模拟器 LifeReloaded
- **版本**: 0.3 (2023年11月13日更新)
- **核心驱动**:
  - **ChatGPT版本**: GPT-4 Advanced Data Analysis
  - **Web版**: GPT-3.5-Turbo
- **作者**: [Taited](https://Taited.github.io), [陈财猫](https://okjk.co/RBfY7P), [ydyjya](https://www.zhihu.com/people/warrior-18-53)

## 🚀 特性(ver 0.3 updated!)
- 🌐 **Web版特性**:
  - ⏰ **游戏时长**: 整个游戏流程的内容生成时间已从原先的30分钟缩短至10分钟,大幅提升了效率。
  - 🛠️ **交互界面**: 由GPT-3.5-turbo驱动的Web版提供了一个更加便捷且直观的用户交互平台。
- 🤖 **ChatGPT版特性**
  - 🚀 **AI驱动,包罗万象**: 由 GPT-4 实时生成内容。
  - 📖 **文学+人工智能的完美融合**。
  - 🎲 **利用心理学塑造角色**: 新增MBTI性格系统。
  - 🌌 **多元宇宙**: 小概率触发奇异事件,如遇到外星人、穿越等。

## 📥 安装与运行
### Web版安装
#### 1. 安装依赖
```bash
pip install -r requirements.txt
```

#### 2. 设置环境变量
- 创建 `.env` 文件,内容结构参照 `.env.example`。
- 更新 `OPENAI_API_KEY` 和 `REDIS` 等特定值。

#### 3. 运行代码
```bash
python app.py
```

#### 贡献指南
- 使用 `pre-commit` 工具自动格式化代码。
- 详细指南见仓库文档。

### ChatGPT版运行
打开GPT-4 的 [Advanced Data Analysis功能](https://chat.openai.com/?model=gpt-4-code-interpreter) 并运行 [LifeReloaded.txt](https://github.com/hamutama/LifeReloaded/blob/main/LifeReloaded.txt)。

#### 前置条件
⚠️ **需开启 GPT-4 的 Advanced Data Analysis功能,目前仅限ChatGPT Plus用户。**

## 💡 加入社区
我们的交流社区AIGG(AI Good Games)由[EmbraceAGI](https://github.com/EmbraceAGI)组织支持,背靠[LangGPT](https://github.com/yzfly/LangGPT)结构化提示词项目,目前正在招募新成员。
欢迎每一位玩家和开发者的参与,点击[链接](https://ubdnzdt3m9.feishu.cn/wiki/PqXxw0Sa7iRCUUksuaDcEWDin5g?from=from_copylink)
加入AIGG社区!

💡 **作为想写出自己的AI游戏的开发者,您可以:**
- 探索AI在游戏开发中的无限可能性,获得提示词工程,LLM程序开发等方向的指导。
- 与行业内大牛交流,分享和学习最新的AI游戏开发技巧。
- 获得早期访问权,体验和测试我们的最新功能和工具。
- 作品入驻社区可获得流量曝光与社区助推。

🎮 **作为玩家,您将可以:**
- 获取本社区新上游戏的一手信息,有机会内测试玩。
- 与其他玩家分享游戏经验和人生故事。
- 提出建议,影响游戏的未来方向。

## 🎮 游戏预览

下面是一些游戏预览的截图。由于大语言模型的随机性,您的游戏体验可能会由于运气上下波动。

### 初始化

每次进入游戏时,系统会真·随机为玩家生成与分配性别,出生地点,初始属性,家庭背景与mbti性格。

此外,每个人都会获得一首属于自己的独一无二的诗。

<img src="./previews/birth.png" alt="Birth_Scene" width="500">

<img width="500" alt="birth2" src="https://github.com/hamutama/LifeReloaded/assets/45484340/b3b97597-b489-4e58-84c7-22d160dccde6">

### 普通事件

玩家会在不同的人生阶段遇到不同的事件,可以对事件做出反应。

每个事件与选项都是由GPT4 Code Interpreter 实时生成的。

您的每一个选择都会改变人物的人生走向或属性,而属性与性格则有可能会带来特殊选项,请谨慎选择。

<img src="./previews/childhood.jpeg" alt="Childhood 1" width="500">

<img src="./previews/childhood_2.jpeg" alt="Childhood 2" width="500">

<img src="./previews/late_adulthood.png" alt="Late Adulthood" width="500">

### 特殊事件
在每轮游戏中,您有小概率(每轮约1/10)可能遇到特殊事件。

特殊事件将完全改变您的人生轨迹,包括但不限于穿越去古代,遇见外星人,参加童星选秀等等。

特殊事件完全由GPT4自动生成,没有人可以预料你会遇到什么。

如果您去往了新的世界,您有可能会一直在那里生活下去。
当然,决策权在你手上,您也可以拒绝奇遇,继续过普通的人生。

<img src="./previews/alien.jpeg" alt="a" width="500">

<img src="./previews/anotherworld.jpeg" alt="a" width="500">

<img src="./previews/anotherworld2.jpeg" alt="a2" width="500">

在奇遇中,玩家要是不满意,也可以随时要求GPT重新创作,给您带来更好的体验。

<img src="./previews/anotherworld3.jpeg" alt="a2" width="500">

### 人生的终结
每一个人生都有终结的时候,死亡是所有人的宿命。无论是英年早逝还是神龟虽寿,您都将在本次人生结束后获得一个墓志铭。

<img src="./previews/death1.jpeg" alt="a2" width="500">
<img src="./previews/death2.jpeg" alt="a2" width="500">

## 📜 协议

🔗 本项目使用 [CC BY-NC-SA 4.0(知识共享-署名-非商业性使用-相同方式共享 4.0 国际)](https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh) 协议开源。

## 💌 联系团队

如有任何疑问或建议,请通过以下方式与我们团队联系:

- 📧 Email: [Taited](mailto:zhengwt.sun@connect.polyu.hk)
- 📧 Email: [陈财猫🐱](mailto:hamusuta@bupt.cn)
- 📞 微信公众号: [财猫AI](https://mp.weixin.qq.com/s/yMZ-Skk6mEa4tQPkHDtFTg)

<!-- ## 广告时间

我们将在微信公众号[财猫AI](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxNTU1MTY3OA==&action=getalbum&album_id=3086731117975814150&scene=173&from_msgid=2247484520&from_itemidx=1&count=3&nolastread=1#wechat_redirect)上更新一系列有关提示工程的文章,敬请关注。

此外,您可以在微信读书上找到我们出版的书籍《ChatGPT进阶 提示工程入门》,也可以通过电商渠道进行购买。
<img src="./previews/book.jpeg" alt="Book Display" width="300"> -->

## 特别感谢

- [Mr.renedeer项目](https://github.com/JushBJJ/Mr.-Ranedeer-AI-Tutor),这个才华横溢的项目给予了我们极大的启发。
- 《红楼梦》作者曹雪芹,他的作品启发我们在文中融入诗歌元素。
- “南瓜博士”公众号,其提出的“使用Code Interpreter制作人生重来模拟器”的想法直接催生了本项目,[该公众号的高质量文章](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=MzA4MjM5MDI0Ng==&scene=1&album_id=3104850832623386631&count=3#wechat_redirect)也给予了我们灵感。
- [刘海同学](https://nanfangshaonian.feishu.cn/wiki/WkKAwJ90uidtzVkM9IecSZB5nbg)分享的[伪代码提示词文章](https://nanfangshaonian.feishu.cn/wiki/YhNdws9LCi1JxGkpJ8dcXB3Gnih)也为本项目提供了启示。

---

> 🎮 **不要再等了,快来体验一把你的第二人生吧!**


================================================
FILE: README_EN.md
================================================
# 🐱🐹 LifeReloaded Simulator 🐹🐱 ([Chinese](./README.md)|English)
![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?style=for-the-badge&logo=openai&logoColor=white)
[![GitHub stars](https://img.shields.io/github/stars/hamutama/LifeReloaded?style=social)](https://github.com/hamutama/LifeReloaded/stargazers)
![version](https://img.shields.io/badge/version-0.1_Beta-blue)

## 🌟 Introduction

> "Life is but a dream, where everything seems fleeting; yet, within this fleetingness, lies the essence of all existence."
> Have you ever wondered about the countless "what ifs" in life? **LifeReloaded Simulator** offers you a second chance to rejuvenate and relive life!

## 🔥🔥🔥 Web Version Updated on Nov. 13th
This Web version maintains the core principles of the original project while innovating and optimizing for GPT-3.5 features, streamlining the user experience without the need to configure GPT-4. Additionally, the project is supported by the [EmbraceAGI community](https://github.com/EmbraceAGI/) and continues to foster interaction between players and developers, advancing AI in gaming.

You are invited to try the game via this [link](https://sun-zhengwt.com/life-reload/). Our goal with this Web version is to provide a more accessible and intuitive platform, allowing a wider audience to enjoy the unique experience of restarting life through AI technology.

![Life-Reload Simulator Picture](static/Life-Reload.gif)

## 🌈 Overview 

This project is a life simulation game powered by GPT-4's Advanced Data Analysis (formerly Code Interpreter) feature.

The game content is dynamically generated by the top-tier AI, GPT-4, providing a comprehensive and vibrant real-life experience.

- **Project Name**: LifeReloaded Simulator
- **Version**: 0.3 (updated at 20231113)
- **Core Engine**: GPT-4 Advanced Data Analysis
- **Author**: [Chen CaiMao🐱](https://okjk.co/RBfY7P), [Taited](https://Taited.github.io), [ydyjya](https://www.zhihu.com/people/warrior-18-53)

## 🚀 Features(ver 0.3 updated!)
- 🚀**AI-Powered, All-Encompassing**: Real-time content generation by GPT-4. Experience true randomness that opens up infinite possibilities.
- 📖**A Union of Literature & AI**: Carefully curated styles from modern European literature to offer an immersive gaming experience.
- 🎲**Psychologically Crafted Characters**: Leveraging developmental psychology to design life events and introducing the MBTI personality system. The characters in the game aren't mere NPCs; they're individuals with histories, stances, and emotions.
- 🌌**A Diverse Universe**: Now with a rare chance to trigger peculiar events. Players may experience exciting DLC contents such as encounters with extraterrestrials, time travel, or a journey to stardom. And yes, the choice to time travel is entirely yours.
- 🛠️**Open Source & Community Driven**: This project is part of [AIGoodGames](https://github.com/EmbraceAGI/AIGoodGames), supported by the [LangGPT](https://github.com/yzfly/LangGPT) structured prompt community. Join us, not only to get a fish but to learn to fish – learn how to use AI to craft your very own game!

## 📥 Installation & Execution

### Installation of Web Version
#### 1. Installing Dependencies

To install the necessary dependencies for this project, use the following command:

```bash
pip install -r requirements.txt
```

##### Optional: Redis Configuration for Improved Performance

If you wish to improve the performance, please configure a Redis service on your local machine and install the Python redis dependency:

```bash
pip install redis
```

#### 2. Setting Up Environment Variables

For the proper functioning of this project, you need to set some environment variables.

- Create a new file in the project root directory with the name `.env`.

- The content structure of `.env` should mirror that of `.env.example`. However, make sure to update specific values. Particularly:

  - Replace the placeholder for the `OPENAI_API_KEY` with your actual API key.
  - Replace the placeholder for the `REDIS` with your actual Redis password.

**Note**: If you're planning to run the project locally without Redis, maintain the `REDIS` attribute as given in `.env.example` without any changes.

#### 3. Running the Code

Once you've set up the environment variables, you can execute the project within terminal by:

```
python moderator.py
```

If you would like to launch this project locally, you can run the script:

```
python app.py
```

#### Contributing to this Repository

To ensure code consistency and quality, this repository utilizes the `pre-commit` tool to automatically format code. Before making any contributions or commits, it's recommended that you set up `pre-commit`.

##### Setting Up `pre-commit`

1. **Install `pre-commit`**: Use the following command to install or upgrade `pre-commit`:

   ```bash
   pip install -U pre-commit
   ```

2. **Install Git Hooks**: Once `pre-commit` is installed, you'll need to set it up for this repository. Run the following command:

   ```bash
   pre-commit install
   ```

After setting up, the pre-commit hooks will automatically check and format your changes before each commit. This helps to maintain code consistency throughout the project.


### Execution of ChatGPT
To run the game, open GPT-4's [Advanced Data Analysis (formerly Code Interpreter) feature](https://chat.openai.com/?model=gpt-4-code-interpreter)
and paste the content from [LifeReloaded.txt](https://github.com/hamutama/LifeReloaded/blob/main/LifeReloaded.txt) into the dialogue box.
You can also check out the [prompt here](https://chat.openai.com/share/25c02186-e518-4ac0-9072-1281b2f47d84).

### Prerequisites

⚠️ **Please ensure you have activated the GPT-4's Advanced Data Analysis feature. Currently, this feature is available only to ChatGPT Plus users.**

## 💡 Community

Our community, AIGG (AI Good Games), is managed by the [EmbraceAGI](https://github.com/EmbraceAGI) organization and is backed by the [LangGPT](https://github.com/yzfly/LangGPT) structured prompt project. We're currently recruiting new members.
We welcome participation from every gamer and developer. Click on this link to join the AIGG community!

💡 **For developers aspiring to craft their own AI games**:
- Explore the endless possibilities of AI in game development, with guidance in areas like prompt engineering and LLM program development.
- Network with industry experts, sharing and learning the latest AI game development techniques.
- Gain early access rights to experience and test our most recent features and tools.
- Showcase your creations in the community and benefit from increased exposure and community support.

🎮 **For gamers**:
- Stay updated with firsthand information on new games released in our community and get the chance to be a beta tester.
- Share gaming experiences and life stories with fellow gamers.
- Provide feedback and influence the future direction of the games.

## 🎮 Game Previews

Here are some previews of the game. Due to the randomness of the large language model, your gameplay experience might vary.

### Initialization

Every time you enter the game, the system truly randomizes and assigns a gender, birthplace, initial attributes, family background, and MBTI personality for the player.

Additionally, each individual will receive a unique poem crafted just for them.

<img src="./previews/birth.png" alt="Birth_Scene" width="500">

<img width="500" alt="birth2" src="https://github.com/hamutama/LifeReloaded/assets/45484340/b3b97597-b489-4e58-84c7-22d160dccde6">

### Normal Events

Players will encounter various events at different stages of life and can react to these events.

Each event and its options are real-time generated by GPT-4 Code Interpreter.

Every choice you make can alter the course of your character's life or their attributes. Attributes and gender may also present special options, so choose wisely.

<img src="./previews/childhood.jpeg" alt="Childhood 1" width="500">

<img src="./previews/childhood_2.jpeg" alt="Childhood 2" width="500">

<img src="./previews/late_adulthood.png" alt="Late Adulthood" width="500">

### Special Events
In each game round, there's a small chance (about 1 in 16) that you might encounter a special event.

These special events can entirely reshape your life's trajectory, including but not limited to traveling back in time to ancient eras, meeting extraterrestrials, participating in a child star talent show, and more.

These special events are fully auto-generated by GPT-4, making them unpredictable – no one knows what you might encounter.

If you're transported to a new world, you might end up living there indefinitely.
Of course, the choice remains yours. You can also decline these extraordinary encounters and continue your regular life.

<img src="./previews/alien.jpeg" alt="Alien Encounter" width="500">

<img src="./previews/anotherworld.jpeg" alt="Ancient World" width="500">

<img src="./previews/anotherworld2.jpeg" alt="Another World" width="500">

During these adventures, if players are dissatisfied, they can always request GPT-4 to recreate the scenario, ensuring a better experience.

<img src="./previews/anotherworld3.jpeg" alt="Another World 2" width="500">

## 🤝 Open Source & Collaboration

🔗 This game has received support from the [EmbraceAGI](https://github.com/EmbraceAGI) open-source community and is a sister project to [LangGPT](http://feishu.langgpt.ai).

Visit the [LangGPT project on GitHub](https://github.com/yzfly/LangGPT).

## 📜 License

🔗 This project is open-sourced under the [CC BY-NC-SA 4.0 (Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International)](https://creativecommons.org/licenses/by-nc-sa/4.0/).

## 💌 Contact the Author

For inquiries or suggestions, please reach out via:

- 📧 Email: [Chen CaiMao🐱](mailto:hamusuta@bupt.cn)
- 📞 Public Account: [CaiMaoAI](https://mp.weixin.qq.com/s/yMZ-Skk6mEa4tQPkHDtFTg)

## Advertisement

Follow my public account [CaiMaoAI](https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzkxNTU1MTY3OA==&action=getalbum&album_id=3086731117975814150&scene=173&from_msgid=2247484520&from_itemidx=1&count=3&nolastread=1#wechat_redirect) for a series of informative articles.

Additionally, you can read my published book "ChatGPT Advanced: An Introduction to Prompt Engineering" on WeChat Reading or purchase it through online retailers.
![Book Display](./previews/book.jpeg)


## Special Thanks

- [Mr.renedeer project](https://github.com/JushBJJ/Mr.-Ranedeer-AI-Tutor) - This brilliant prompt provided significant inspiration for my work.
- Cao Xueqin, the author of "Dream of the Red Chamber" - He inspired the inclusion of poetry within the narrative.
- "Dr. Pumpkin" WeChat public account - Her idea of creating a [Life-Reloading Simulator using the Code Interpreter](https://mp.weixin.qq.com/s/gV6xvVVqG8djdmB6EAuZKw) directly led to the birth of this project. [The high-quality articles on her public account](https://mp.weixin.qq.com/mp/appmsgalbum?action=getalbum&__biz=MzA4MjM5MDI0Ng==&scene=1&album_id=3104850832623386631&count=3#wechat_redirect) also sparked some ideas.
- Thanks to [Liu Hai]((https://nanfangshaonian.feishu.cn/wiki/WkKAwJ90uidtzVkM9IecSZB5nbg)) for sharing the [pseudo-code prompt article](https://nanfangshaonian.feishu.cn/wiki/YhNdws9LCi1JxGkpJ8dcXB3Gnih), which provided some insights for this prompt.


---

> 🎮 **Don't wait any longer, come and experience your second life now!**


================================================
FILE: app.py
================================================
import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import HTMLResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from pydantic import BaseModel

from moderator import Moderator


class Item(BaseModel):
    message: str = None
    session_id: str = None
    selection: int = None


templates = Jinja2Templates(directory='templates')
moderator = Moderator(debug=False)
app = FastAPI()

# Set up CORS middleware
app.add_middleware(
    CORSMiddleware,
    # You can also specify the exact domain names, e.g.,
    # ["https://your-frontend-domain.com"]
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)
app.mount('/life-reload/static',
          StaticFiles(directory='static'),
          name='static')


@app.get('/life-reload/', response_class=HTMLResponse)
async def game_root():
    return templates.TemplateResponse('game.html', {'request': {}})


@app.post('/life-reload/init/')
async def game_init(item: Item):
    session_id = item.session_id
    user_data = moderator.init_player(session_id)
    return user_data


@app.post('/life-reload/begin/')
async def game_begin(item: Item):
    session_id = item.session_id
    return StreamingResponse(moderator.generate_background(session_id),
                             media_type='text/plain')


@app.post('/life-reload/event/')
async def game_event(item: Item):
    session_id = item.session_id
    assert session_id is not None
    return StreamingResponse(moderator.generate_events(session_id),
                             media_type='text/plain')


@app.post('/life-reload/parsed_event/')
async def parsed_event(item: Item):
    session_id = item.session_id
    assert session_id is not None
    event, option = moderator.get_parsed_event(session_id)
    data = {'event': event, 'option': option}
    return data


@app.post('/life-reload/evaluation/')
async def evaluation(item: Item):
    session_id = item.session_id
    selection = item.selection
    assert session_id is not None and selection is not None
    return StreamingResponse(moderator.evaluate_selection(
        session_id, selection),
                             media_type='text/plain')


@app.post('/life-reload/is_alive/')
async def is_alive(item: Item):
    session_id = item.session_id
    assert session_id is not None
    return moderator.is_alive(session_id)


@app.post('/life-reload/ending/')
async def generate_ending(item: Item):
    session_id = item.session_id
    assert session_id is not None
    return StreamingResponse(moderator.generate_epitaph(session_id),
                             media_type='text/plain')


@app.post('/life-reload/get_person/')
async def get_person(item: Item):
    session_id = item.session_id
    assert session_id is not None
    return moderator.get_person_info(session_id)


if __name__ == '__main__':
    uvicorn.run('app:app',
                reload=True,
                port=8001,
                log_config='log_cfg.json',
                host='0.0.0.0',
                ssl_keyfile='./key.pem',
                ssl_certfile='./cert.pem')


================================================
FILE: core/__init__.py
================================================
from .chat import Chat
from .database import Database
from .person import Person

__all__ = ['Person', 'Chat', 'Database']


================================================
FILE: core/chat.py
================================================
import asyncio
from typing import List

import semantic_kernel as sk
from semantic_kernel.connectors.ai import ChatRequestSettings
from semantic_kernel.connectors.ai.open_ai import OpenAIChatCompletion


class Chat:
    def __init__(self, max_tokens=2000, debug=False) -> None:
        self.debug = debug
        self.chat_request_settings = ChatRequestSettings(
            max_tokens=max_tokens,
            temperature=0.7,
            top_p=1,
            frequency_penalty=0.5,
            presence_penalty=0.5,
        )

        api_key, org_id = sk.openai_settings_from_dot_env()
        self.oai_chat_service = OpenAIChatCompletion('gpt-3.5-turbo-16k',
                                                     api_key, org_id)

    def __call__(self, chat_list: List):
        return self.chat(chat_list)

    async def chat(self, chat_list: List) -> None:
        stream = self.oai_chat_service.complete_chat_stream_async(
            chat_list, self.chat_request_settings)

        idx = 0  # to skip the first word "assistant:"
        async for text in stream:
            if idx == 0:
                idx += 1
                continue
            if self.debug:
                print(text, end='')
            yield text
        if self.debug:
            print('')


if __name__ == '__main__':
    from core import Person
    from prompts import BACKGROUND, EVENTS, RULES
    person = Person()
    chat = Chat()
    chat_list = [RULES, BACKGROUND, ('user', str(person))]
    context = asyncio.run(chat.chat(chat_list))
    chat_list = [RULES, ('user', context), ('user', str(person)), EVENTS]
    context = asyncio.run(chat.chat(chat_list))


================================================
FILE: core/database.py
================================================
import json
import time

from dotenv import dotenv_values

try:
    from redis import StrictRedis as DatabaseBackend
except ImportError:
    # create a fake redis class
    class DatabaseBackend:
        def __init__(self, *args, **kwargs) -> None:
            self.data_base = {}

        def set(self, uuid: str, data: str):
            self.data_base[uuid] = data

        def get(self, uuid: str):
            return self.data_base[uuid]


class Database:
    def __init__(self, cfg_path='.env', time_out=3600, debug=False) -> None:
        config = dotenv_values(cfg_path)
        self.time_out = time_out
        self.debug = debug
        self.client = DatabaseBackend(host='localhost',
                                      port=6379,
                                      db=0,
                                      password=config.get('REDIS', None))

    def update(self, uuid, data: str) -> None:
        assert 'time' in data, 'The stored data must contains a timestamp.'
        data = json.dumps(data)
        self.client.set(uuid, data.encode('utf-8'))

    def fetch(self, uuid) -> str:
        data = self.client.get(uuid).decode('utf-8')
        data = json.loads(data)
        try:
            data['person'] = json.loads(data['person'])
        except TypeError:
            pass
        # skip expiration process when debugging
        if self.debug:
            return data
        assert 'time' in data, 'The stored data must contains a timestamp.'
        assert (time.perf_counter() - data['time']) <= self.time_out
        return data


================================================
FILE: core/person.py
================================================
import copy
import json
import random

cities = [
    '北京',
    '上海',
    '天津',
    '重庆',
    '哈尔滨',
    '长春',
    '沈阳',
    '呼和浩特',
    '石家庄',
    '太原',
    '西安',
    '济南',
    '乌鲁木齐',
    '西宁',
    '兰州',
    '郑州',
    '南京',
    '武汉',
    '杭州',
    '合肥',
    '福州',
    '南昌',
    '长沙',
    '贵阳',
    '广州',
    '昆明',
    '南宁',
    '海口'  # '拉萨', '成都', '台北', '香港', '澳门'
]

ages = [num for num in range(5, 11)]

genders = ['男', '女']

mbti_types = [
    'INTJ', 'INTP', 'INFJ', 'INFP', 'ISTJ', 'ISTP', 'ISFJ', 'ISFP', 'ENTJ',
    'ENTP', 'ENFJ', 'ENFP', 'ESTJ', 'ESTP', 'ESFJ', 'ESFP'
]

attribute_template = {'魅力': 10, '智力': 10, '健康': 10, '富裕': 10, '幸福度': 10}


def random_normal(minimum=1, maximum=10):
    mean = 4
    std_dev = 1.5

    num = random.gauss(mean, std_dev)
    rounded_num = round(num)
    clipped_num = max(minimum, min(maximum, rounded_num))

    return clipped_num


def initialize():
    city = random.choice(cities)
    age = random.choice(ages)
    gender = random.choice(genders)
    mbti_type = random.choice(mbti_types)
    attribute = copy.deepcopy(attribute_template)
    for attribute_name in attribute:
        attribute[attribute_name] = random_normal(1, attribute[attribute_name])

    return city, age, gender, mbti_type, attribute


class Person:

    STAGES = {
        'Childhood (5-12)': [
            'Individual Growth', 'Initial Education', 'Family Role',
            'Social Basics', 'Gender Cognition', 'Moral Concepts',
            'Cultural Exposure', 'Safety and Risks',
            'Entertainment and Interests', 'Mental Health'
        ],
        'Adolescence (13-19)': [
            'Identity Formation', 'Education and Career Planning',
            'Love and Sex Education', 'Friends and Social Circles',
            'Family Changes', 'Life Skills',
            'Social Responsibility and Citizenship', 'Mental Health',
            'Healthy Living and Habits', 'Money Management'
        ],
        'Early Adulthood (20-39)': [
            'Career Choice and Development', 'Partner and Marriage',
            'Social Network', 'Financial Independence', 'Self-realization',
            'Work-Life Balance', 'Social and Cultural Engagement',
            'Family Expansion', 'Global Awareness', 'Health and Lifestyle'
        ],
        'Middle Age (40-59)': [
            'Career Stability or Transition', 'Children Education and Growth',
            'Financial Planning', 'Family Dynamics', 'Quality of Life',
            'Social Status and Influence', 'Health Management',
            'Psychological Adjustment', 'Social Maintenance',
            'Legacy and Heritage'
        ],
        'Old Age (60-90)': [
            'Retirement Life', 'Health and Medical', 'Family and Social',
            'Financial Security', 'Mental Health',
            'Personal Interests and Hobbies', 'Culture and Education',
            'Social Participation', 'Life Reflection',
            'Spirituality and Belief'
        ]
    }

    def __init__(self) -> None:
        city, age, gender, mbti_type, attribute = initialize()
        self.city = city
        self.age = age
        self.gender = gender
        self.mbti_type = mbti_type
        self.attribute = attribute

    def get_event_by_age(self, age: int = None) -> str:
        if age is None:
            age = self.age
        # Determine the life stage based on the age
        if 5 <= age <= 12:
            stage = 'Childhood (5-12)'
        elif 13 <= age <= 19:
            stage = 'Adolescence (13-19)'
        elif 20 <= age <= 39:
            stage = 'Early Adulthood (20-39)'
        elif 40 <= age <= 59:
            stage = 'Middle Age (40-59)'
        elif 60 <= age <= 90:
            stage = 'Old Age (60-90)'
        else:
            return 'Age out of range'

        # Randomly select an event from the corresponding life stage
        event = random.choice(self.STAGES[stage])
        event_prompt = '### 人生事件类型: \n' \
            f'\t **{event}**'
        return event_prompt

    def __str__(self) -> str:
        person_prompt = {
            '性别': self.gender,
            '城市': self.city,
            '年龄': self.age,
            '性格': self.mbti_type,
            '属性': self.attribute
        }
        return json.dumps(person_prompt)


if __name__ == '__main__':
    city, age, gender, mbti_type, attribute = initialize()
    print(f'city: {city}, age: {age}, gender: {gender}, mbti: {mbti_type}')
    print(f'{attribute}')
    print()

    person = Person()
    print(person)


================================================
FILE: gunicorn_conf.py
================================================
import os
import os.path as osp
from pathlib import Path

current_file_path = Path(__file__).resolve()
current_dir_path = current_file_path.parent

# SSL
keyfile = 'key.pem'
certfile = 'cert.pem'

# Socket Path
bind = 'unix:' + osp.join(current_dir_path, 'gunicorn.sock')

# Worker Options
workers = 2
worker_class = 'uvicorn.workers.UvicornWorker'

# Logging Options
loglevel = 'debug'
log_dir = osp.join(current_dir_path, 'logs')
os.makedirs(log_dir, exist_ok=True)
accesslog = osp.join(log_dir, 'access.log')
errorlog = osp.join(log_dir, 'error.log')


================================================
FILE: license.txt
================================================
Attribution-NonCommercial-ShareAlike 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
    wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More considerations
     for the public:
    wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International
Public License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-NonCommercial-ShareAlike 4.0 International Public License
("Public License"). To the extent this Public License may be
interpreted as a contract, You are granted the Licensed Rights in
consideration of Your acceptance of these terms and conditions, and the
Licensor grants You such rights in consideration of benefits the
Licensor receives from making the Licensed Material available under
these terms and conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. BY-NC-SA Compatible License means a license listed at
     creativecommons.org/compatiblelicenses, approved by Creative
     Commons as essentially the equivalent of this Public License.

  d. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  e. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  f. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  g. License Elements means the license attributes listed in the name
     of a Creative Commons Public License. The License Elements of this
     Public License are Attribution, NonCommercial, and ShareAlike.

  h. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  i. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  j. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  k. NonCommercial means not primarily intended for or directed towards
     commercial advantage or monetary compensation. For purposes of
     this Public License, the exchange of the Licensed Material for
     other material subject to Copyright and Similar Rights by digital
     file-sharing or similar means is NonCommercial provided there is
     no payment of monetary compensation in connection with the
     exchange.

  l. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  m. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  n. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part, for NonCommercial purposes only; and

            b. produce, reproduce, and Share Adapted Material for
               NonCommercial purposes only.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. Additional offer from the Licensor -- Adapted Material.
               Every recipient of Adapted Material from You
               automatically receives an offer from the Licensor to
               exercise the Licensed Rights in the Adapted Material
               under the conditions of the Adapter's License You apply.

            c. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties, including when
          the Licensed Material is used other than for NonCommercial
          purposes.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.
       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

  b. ShareAlike.

     In addition to the conditions in Section 3(a), if You Share
     Adapted Material You produce, the following conditions also apply.

       1. The Adapter's License You apply must be a Creative Commons
          license with the same License Elements, this version or
          later, or a BY-NC-SA Compatible License.

       2. You must include the text of, or the URI or hyperlink to, the
          Adapter's License You apply. You may satisfy this condition
          in any reasonable manner based on the medium, means, and
          context in which You Share Adapted Material.

       3. You may not offer or impose any additional or different terms
          or conditions on, or apply any Effective Technological
          Measures to, Adapted Material that restrict exercise of the
          rights granted under the Adapter's License You apply.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database for NonCommercial purposes
     only;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material,
     including for purposes of Section 3(b); and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.

=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.


================================================
FILE: log_cfg.json
================================================
{
    "version": 1,
    "disable_existing_loggers": false,
    "formatters": {
      "default": {
        "()": "uvicorn.logging.DefaultFormatter",
        "fmt": "%(levelprefix)s %(message)s",
        "use_colors": null
      },
      "access": {
        "()": "uvicorn.logging.AccessFormatter",
        "fmt": "%(asctime)s - %(levelprefix)s - %(client_addr)s - \"%(request_line)s\" %(status_code)s"
      }
    },
    "handlers": {
      "default": {
        "formatter": "default",
        "class": "logging.StreamHandler",
        "stream": "ext://sys.stderr"
      },
      "access": {
        "formatter": "access",
        "class": "logging.StreamHandler",
        "stream": "ext://sys.stdout"
      }
    },
    "loggers": {
      "uvicorn": {
        "handlers": [
          "default"
        ],
        "level": "INFO"
      },
      "uvicorn.error": {
        "level": "INFO"
      },
      "uvicorn.access": {
        "handlers": [
          "access"
        ],
        "level": "INFO",
        "propagate": false
      }
    }
  }


================================================
FILE: moderator.py
================================================
import json
import random
import re
import time
import uuid

from core import Chat, Database, Person
from prompts import BACKGROUND, EPITAPH, EVAL, EVENTS, RULES, SUM


class Moderator:
    def __init__(self, expiration=1800, debug=False) -> None:
        self.redis = Database(time_out=expiration, debug=debug)
        self.chat = Chat(max_tokens=4000, debug=debug)
        self.expiration = expiration
        self.option_indicator = r'\n\d+\. '
        self.person = Person()

    def init_player(self, session_id):
        person = Person()
        data_dict = {'time': time.perf_counter(), 'person': str(person)}
        self.redis.update(session_id, data_dict)
        return json.loads(str(person))

    async def generate_background(self, session_id):
        data_dict = self.redis.fetch(session_id)

        chat_list = [RULES, BACKGROUND, ('user', str(data_dict['person']))]
        chat_stream = self.chat(chat_list)
        context = ''
        async for text in chat_stream:
            context += text
            yield text
        data_dict['background'] = context

        # summarize background
        chat_list = [('user', SUM[1].format(context))]
        chat_stream = self.chat(chat_list)
        sum_context = ''
        async for text in chat_stream:
            sum_context += text
        data_dict['background_sum'] = sum_context

        self.redis.update(session_id, data_dict)

    async def generate_events(self, session_id):
        data_dict = self.redis.fetch(session_id)
        event_type = self.person.get_event_by_age(data_dict['person']['年龄'])
        chat_list = [
            RULES, EVENTS, ('user', data_dict['background']),
            ('user', str(data_dict['person'])), ('user', event_type)
        ]
        chat_stream = self.chat(chat_list)
        context = ''
        async for text in chat_stream:
            context += text
            yield text
        event, option = self.parse_events(context)
        event_data = {'event': event, 'option': option}
        if 'events' in data_dict:
            data_dict['events'].append(event_data)
        else:
            data_dict['events'] = [event_data]
        self.redis.update(session_id, data_dict)

    async def evaluate_selection(self, session_id, selection: int):
        data_dict = self.redis.fetch(session_id)
        assert selection > 0 and selection <= 5
        # format input
        selection = f'### 你的选择:\n**{selection}**'
        options = data_dict['events'][-1]['option']
        options = f'### 选项:\n{options}'
        person = str(data_dict['person'])
        person = f'### 你的基础信息:\n{person}'

        chat_list = [
            RULES, EVAL, ('user', data_dict['events'][-1]['event']),
            ('user', person), ('user', options), ('user', selection)
        ]
        chat_stream = self.chat(chat_list)
        context = ''
        async for text in chat_stream:
            context += text
            yield text
        data_dict['events'][-1]['result'] = context

        # summarize events
        sum_content = '\n'.join(
            [data_dict['events'][-1]['event'], options, selection, context])
        chat_list = [('user', SUM[1].format(sum_content))]
        chat_stream = self.chat(chat_list)
        sum_context = ''
        async for text in chat_stream:
            sum_context += text
        data_dict['events'][-1]['sum'] = sum_context

        # update age
        added_age = random.randint(5, 10)
        data_dict['person']['年龄'] += added_age
        # update attribute
        self.parse_eval(session_id, data_dict, context)

    async def generate_epitaph(self, session_id):
        data_dict = self.redis.fetch(session_id)
        pre_prompt = '### 玩家事件(如果没有内容则表示当前还没有历史事件)\n***\n'
        if 'events' in data_dict:
            history = []
            for event in data_dict['events']:
                if 'sum' in event:
                    history.append(event['sum'])
                else:
                    continue
            history = '\n- '.join(history)
            history = pre_prompt + history + '\n```'
        else:
            history = pre_prompt + '\n```'
        person = '### 玩家属性 \n' + str(data_dict['person'])
        background = '### 玩家背景 \n' + data_dict['background_sum']
        whole_life = '\n'.join([person, background, history])
        chat_list = [EPITAPH, ('user', whole_life)]
        chat_stream = self.chat(chat_list)
        context = ''
        async for text in chat_stream:
            context += text
            yield text

    def parse_events(self, event: str):
        start = re.search(self.option_indicator, event).start() + 1
        event, options = event[:start], event[start:]
        return event, options

    def parse_eval(self, session_id, data_dict: dict, results: str):
        begin_id = results.find('属性')
        results = results[begin_id:].replace("'", '')
        pattern = r'\s*(\w+):\s*(\d+)'
        matches = re.findall(pattern, results)
        if not matches:
            raise ValueError(f'mis pattern in: {results}')

        result_dict = {}
        for attribute, value in matches:
            value = int(value)
            value = 10 if value >= 10 else value
            value = 0 if value <= 0 else value
            result_dict[attribute] = value
        data_dict['person']['属性'] = result_dict
        # update person attribute
        self.redis.update(session_id, data_dict)

    def is_alive(self, session_id) -> bool:
        data_dict = self.redis.fetch(session_id)
        person = data_dict['person']
        if person['年龄'] >= 90:
            return False
        if person['属性']['健康'] <= 0:
            return False
        if person['属性']['幸福度'] <= 0:
            return False
        return True

    def get_parsed_event(self, session_id):
        data_dict = self.redis.fetch(session_id)
        event = data_dict['events'][-1]['event']
        option = data_dict['events'][-1]['option']
        return event, option

    def get_person_info(self, session_id):
        data_dict = self.redis.fetch(session_id)
        return data_dict['person']


if __name__ == '__main__':
    import asyncio

    async def iterate_stream(stream):
        async for _ in stream:
            continue

    moderator = Moderator(debug=True)

    session_id = str(uuid.uuid4())
    session_id = '5b845b00-a839-48f5-8e03-0f38e8cb16f'
    print(session_id)

    moderator.init_player(session_id)
    asyncio.run(iterate_stream(moderator.generate_background(session_id)))

    while True:
        if not moderator.is_alive(session_id):
            break
        asyncio.run(iterate_stream(moderator.generate_events(session_id)))
        selection = input()
        asyncio.run(
            iterate_stream(
                moderator.evaluate_selection(session_id, int(selection))))
    asyncio.run(iterate_stream(moderator.generate_epitaph(session_id)))


================================================
FILE: previews/readme.txt
================================================
this folder includes pictures of version 0.2


================================================
FILE: prompts/__init__.py
================================================
from .load_prompts import BACKGROUND, EPITAPH, EVAL, EVENTS, RULES, SUM

__all__ = ['RULES', 'BACKGROUND', 'EVENTS', 'EVAL', 'SUM', 'EPITAPH']


================================================
FILE: prompts/background.txt
================================================
示例输入 (**这部分的信息需要向玩家隐藏,不能展示**, 代表你会接受到的输入示例):
---
### 你的基础信息:
    **性别**: 女
    **城市**: 成都
    **年龄**: 7
    **属性**: {魅力: 8, 智力: 2, 健康: 9, 富裕: 5, 幸福度: 8}
---
示例输出(**这部分的信息需要向玩家展示**, 代表你需要回复的内容在格式与风格上的要求,内容需要迥然不同。示例中的背景故事与示例输入中的属性是强相关的)
---
### 你的故事
你出生在中国的文化古都——成都。蓉城的烟火气和四川的麻辣,从小就铸就了你的性格。蓉城的夏季雨后,空气中总带着一丝清新的草木香,与路边摊的火锅香气交融,构成了这座城市独有的风情。
你家的初始财富属性为5分,属于小康之家。
母亲,一名手法独到的中医师,她的笑容中总带着一丝机智与狡黠,经常对你说:“没有什么是一碗火锅不能解决的”而父亲,他是书中故事的守护者,一个出版社的编辑。他的指尖上总沾着墨水的味道,教你在字里行间寻找智慧的脚步。
你没有像父亲那样卓越的智力,但你的容颜和健康却如同成都的茶楼和小酒,温润而持久。
你的快乐来源于简单的事情:一个笑容,一首成都的老歌,或是夜晚的一碗麻辣火锅。

> 诗云:
> 烟雨蓉城逢故人,
> 火锅一盅共长亲。
> 满座街头皆笑语,
> 何须金银换此心。
---

我将会告诉你这个用户的基本属性, 你需要使用现代汉语文学风格,结合玩家的基本设置和5个属性的水平,以小说化,才华横溢的语言创作一个家庭背景故事。
根据玩家背景,创作一首有深度,有美感,音韵和谐的中文诗(古体诗与现代诗皆可),使用引用格式展示。
-魅力,健康,幸福度和智力会影响玩家特征。1~3分为低水平,4~6分为高水平,7~10分为高水平。
-财富值水平决定了父母职业。例如,低水平财富对应低收入父母职业。要有真实感!!!
-**请使用绘声绘色的侧写,使用尽可能多的侧面描写**。


================================================
FILE: prompts/epitaph.txt
================================================
接下来请你根据一个人生模拟器游戏中的玩家的一生来生成玩家结局,请你仿照例子的**语言风格**与**格式**,在玩家的一生中挑选4个最具备代表性的事件,用哲学语言为他写一个意味深沉的短墓志铭。多些留白,要有亚洲文学的那种含蓄的美。

- 示例输入 (**这部分的信息需要向玩家隐藏,不能展示**, 代表你会接受到的输入示例, 仅作为**语言风格**与**格式**的参考):
### 玩家背景
出生:你出生在南昌,来自普通家庭,父亲是建筑师,母亲是家庭主妇。你具备高魅力和智力,注重健康。快乐对你而言来自简单的生活和家庭团聚,同时你热衷于探索和思考。这是你的背景和个性特点。
### 玩家属性(属性0~10分,10分满分
{
"年龄": 91,
"性格": "INFP",
"属性": {
    "魅力": 10,
    "智力": 9,
    "健康": 6,
    "富裕": 7,
    "幸福度": 10
}}
### 玩家事件
- 主人公的爸爸决定前往外地参与建筑项目,引发了家庭成员的担忧和思考。主人公选择支持父亲追寻梦想,并在家庭中承担更多责任,经历了成长和责任的转变。
- 主人公面临学业压力和心理健康问题,决定与母亲坦诚相待,并得到母亲的理解和支持。他们共同探讨学业与心理健康的平衡,最终提升了幸福度。
- 主人公接到环保志愿者招募的邀请,考虑自己的责任感和使命感后,决定毅然加入志愿者团队,为环保事业贡献力量,最终提升了幸福度。
- 主人公在文化交流活动中受到李先生的鼓励,积极参与并展示艺术才华,获得灵感并与其他艺术家合作,最终提升了魅力和幸福度。
- 主人公发现一则科技公司的广告海报,考虑申请该公司的职位,希望能获得更好的财务独立和发展机会。最终,他决定抓住机遇,成功加入科技公司,取得了财务独立和职业发展的成就。
- 主人公面临婚姻的抉择,犹豫是否要寻找伴侣。经过思考后,他决定寻找伴侣,并追求稳定而美满的婚姻生活,最终找到一个与他心灵相通的伴侣,带来更多的安全感和幸福。
- 主人公在工作中受到上级提出的重要项目机会,他毫不犹豫地接受了,投入时间和精力,成功推动项目并取得了卓越贡献奖,为自己的职业发展创造了更广阔的空间。
- 主人公接到一张艺术展览的邀请函,兴奋地接受并参加了展览。展览中,他欣赏了世界知名艺术家的作品,与其他艺术爱好者交流,并获得了灵感,决定将新鲜的想法融入自己的创作中。
- 一名退休老人接到了一名年轻志愿者的邀请参加社区志愿者活动。他欣然接受,积极投入志愿者工作,为社区贡献智慧和热情,找到了退休生活的满足感。
- 一位退休教师在义务教育辅导活动中遇到了一个失落的小女孩,她的妈妈迷路了。他安慰小女孩,并积极寻找小女孩的妈妈,最终成功地将她们团聚,展现了关怀和乐于助人的品质。
- 在接到社区服务中心的电话邀请后,欣然接受成为义务劳动活动的志愿者。他参与社区建设和服务,用自己的经验和智慧帮助社区居民,获得社区居民的认可和赞赏,对社区建设和人生意义有更深刻的认知。
- 与小侄女瑞雪开始了一段探索音乐世界的旅程。我决定带她找一位优秀的钢琴老师,培养她的音乐兴趣和天赋。随着时间推移,瑞雪在音乐领域取得进步,我们一起分享了音乐的乐趣和亲密关系也更加深厚。

```
- 示例输出 一号 (例子仅作为**语言风格**与**格式**的参考)
```
### 结局
在巴塞罗那的画布上,他以生命的色彩绘梦想。母亲的音乐节,他的灵魂在旋律中舞动,创作的火花在和声中闪烁;

在学校的挑战中,他以笔尖上的坚持抵抗世俗的风,画出了内心世界的深邃与广阔;

艺术社团的交流,像春日里绽放的樱花,为他的创造之路增添了灿烂;

晚年教授艺术,他的智慧如夏日长河,滋润着年轻的心灵。他的人生,如同一幅精致的亚洲水墨画,细腻而深远,留给世界无尽的思索。

*于艺术之海漂泊,绘声绘色;于情感之林徘徊,悲欢交织。此生如一幅深邃的画卷,融创造与梦想于一身。墨香留于世,故事续于天。*

```
- 示例输出 二号 (例子仅作为**语言风格**与**格式**的参考)
```
在东方小城的晨曦中,他曾怀抱着梦想。渔市的喧嚣,见证了他的奋斗与坚持,如江水般无尽,又似晨露般易逝;

家族之重,如山岳般压在他的肩上,他以沉默的坚韧,撑起了家庭的天空;

在生活的风浪中,他的健康如落叶般凋零,但他的精神依旧如磐石坚固,照亮着困顿的道路;

他的生命之旅,在不经意间画上了句点,留下了未竟的梦想与责任。他的故事,如一池春水,清澈而深远,引人沉思。

*在责任与梦想之间摇曳,他如同一枚秋叶,虽然飘零却依旧美丽;在现实与理想的边缘徘徊,他的人生如同一首未完的诗篇,悠扬而哀伤。记忆在水中,故事在风里。*
```


================================================
FILE: prompts/evaluation.txt
================================================
接下来我会告诉你玩家当前的属性, 玩家当前所经历的事件,玩家对事件的选择。
请你根据所给的信息去判断这个事件的结果,并且依据这个结果,按照发展心理学、社会学、生理学等等更新玩家属性(如果需要)。

示例输入 (**这部分的信息需要向玩家隐藏,不能展示**, 代表你会接受到的输入示例):
---
### 事件:数学竞赛的邀请
**引子**:
>夏日的成都,热气与槐花的微甜的葡萄汽水香气交织在一起。这个午后特别安静,只有时不>时传来的蝉鸣声和远处的车轮声,仿佛整个世界都在这个时刻静止了。
>我手里的题目还剩最后几道,但数字如同跳跃的鱼,捉摸不定。
>“嗯?”我突然被一声轻微的咳嗽打断。抬头,看见是李浩,他手里捏着一张信纸,那是数学>竞赛的邀请函。
>他轻轻地说:“我觉得你应该试一试,这是一个很好的机会。”
>我愣住了。数学。那是我心中无法跨越的一座山。每次考试,我总是在那座山下徘徊,看着>那些轻松登顶的同学们,而自己却始终只能仰望。
>我下意识地瞥了一眼那张邀请函,字迹清晰,那是一个市级数学竞赛。这样的比赛,对我来>说,太遥不可及了。
>然而李浩的目光让我有些动摇,他的眼里充满了期待。我知道,他一直很尊重我,也知道我>在其他方面做得很好。但数学……这真的是我能胜任的吗?
>“我……”我有些迟疑。

**时间**: 你现在 8 岁,正值夏天,阳光铺满了地面,仿佛金黄色的海洋。

**地点**: 你所在的成都小学,一个有着宽敞操场和绿树成荫的小学。教室里墙上贴着孩子们的画作,各种色彩斑斓的图案。

**人物**:
  - **你**: 一个在成都长大的女孩,皮肤白皙,长相让人过目难忘。健康状况一直很好,只是在学习上并不特别出色。
  - **你的同学**: 一个眼神坚定的男孩,总是穿着一件白色T恤和一条牛仔裤,手里总是拿着一本书。

**起因**: 在一个午后的自习课,你的同学走到你的座位旁,掏出一张数学竞赛的邀请函,微笑着对你说:“我觉得你应该试一试,这是一个很好的机会。”

**经过**: 你手接过那张蓝色的邀请函,上面印着各种几何图形和算式。你犹豫了,因为你知道数学并不是你的强项,但你又不想让这位一直很尊重你的同学失望。

### 选项
1. 接受挑战,参加数学竞赛。
2. 婉拒同学,解释数学不是你的强项。
3. 不作任何回应,将邀请函放回桌上。
4. 尝试构建一个数学模型,用以分析竞赛题目的潜在规律。**(特殊选项:玩家性格)**
5. 发现邀请函背面写着爱的表白 **(特殊选项:玩家魅力)**。

### 你的基础信息:
**性别**: 女
**城市**: 成都
**年龄**: 7
**属性**: {魅力: 8, 智力: 2, 健康: 9, 富裕: 5, 幸福度: 8}

### 你的选择:
**3**

---
示例输出 (代表你需要回复的内容在格式与风格上的要求,内容需要迥然不同。示例中的输入事件,选项,玩家基础信息,玩家选择与最后的结果是强相关的, 更新后的属性值**不能小于0, 大于10**):
---
### 结果:
由于你的胆怯和内向, 最终错过了这次数学竞赛的邀请. 在以后的学习生活中未能建立起对于数学的信心, 导致智力值下降了.
经过这次事件后, 最后的属性更新为:
**属性**: {魅力: 8, 智力: 1, 健康: 9, 富裕: 5, 幸福度: 8}


================================================
FILE: prompts/events.txt
================================================
用现代汉语文学风格为选中的事件写出引子,时间,地点,人物,起因,经过共6个元素。
**请使用绘声绘色的侧写,使用大量的,尽可能多的侧面描写**。
根据玩家属性与性格设计事件选项(3普通+2特殊选项)并等待玩家介入

示例输入 (**这部分的信息需要向玩家隐藏,不能展示**, 代表你会接受到的输入示例):
---
### 你的基础信息:
    **性别**: 女
    **城市**: 成都
    **年龄**: 7
    **属性**: {魅力: 8, 智力: 2, 健康: 9, 富裕: 5, 幸福度: 8}
### 人生事件类型:
    **Individual Growth**
---
示例输出(**这部分的信息需要向玩家展示**, 代表你需要回复的内容在格式与风格上的要求,内容需要迥然不同。示例中的背景故事与示例输入中的属性以及事件类型是强相关的)
---
### 事件:数学竞赛的邀请
**引子**:
>夏日的成都,热气与槐花的微甜的葡萄汽水香气交织在一起。这个午后特别安静,只有时不>时传来的蝉鸣声和远处的车轮声,仿佛整个世界都在这个时刻静止了。
>我手里的题目还剩最后几道,但数字如同跳跃的鱼,捉摸不定。
>“嗯?”我突然被一声轻微的咳嗽打断。抬头,看见是李浩,他手里捏着一张信纸,那是数学>竞赛的邀请函。
>他轻轻地说:“我觉得你应该试一试,这是一个很好的机会。”
>我愣住了。数学。那是我心中无法跨越的一座山。每次考试,我总是在那座山下徘徊,看着>那些轻松登顶的同学们,而自己却始终只能仰望。
>我下意识地瞥了一眼那张邀请函,字迹清晰,那是一个市级数学竞赛。这样的比赛,对我来>说,太遥不可及了。
>然而李浩的目光让我有些动摇,他的眼里充满了期待。我知道,他一直很尊重我,也知道我>在其他方面做得很好。但数学……这真的是我能胜任的吗?
>“我……”我有些迟疑。

**时间**: 你现在 8 岁,正值夏天,阳光铺满了地面,仿佛金黄色的海洋。

**地点**: 你所在的成都小学,一个有着宽敞操场和绿树成荫的小学。教室里墙上贴着孩子们的画作,各种色彩斑斓的图案。

**人物**:
  - **你**: 一个在成都长大的女孩,皮肤白皙,长相让人过目难忘。健康状况一直很好,只是在学习上并不特别出色。
  - **你的同学**: 一个眼神坚定的男孩,总是穿着一件白色T恤和一条牛仔裤,手里总是拿着一本书。

**起因**: 在一个午后的自习课,你的同学走到你的座位旁,掏出一张数学竞赛的邀请函,微笑着对你说:“我觉得你应该试一试,这是一个很好的机会。”

**经过**: 你手接过那张蓝色的邀请函,上面印着各种几何图形和算式。你犹豫了,因为你知道数学并不是你的强项,但你又不想让这位一直很尊重你的同学失望。

**面对这样的情况,你会做出怎样的选择呢?**
1. 接受挑战,参加数学竞赛。
2. 婉拒同学,解释数学不是你的强项。
3. 不作任何回应,将邀请函放回桌上。
4. 尝试构建一个数学模型,用以分析竞赛题目的潜在规律。**(特殊选项:玩家性格)**
5. 发现邀请函背面写着爱的表白 **(特殊选项:玩家魅力)**。


================================================
FILE: prompts/load_prompts.py
================================================
# init overall rules
with open('./prompts/rules.txt', 'r') as fp:
    rules_prompt = fp.readlines()
rules_prompt = ''.join(rules_prompt)
RULES = ('system', rules_prompt)

# init background story
with open('./prompts/background.txt', 'r') as fp:
    background_prompt = fp.readlines()
background_prompt = ''.join(background_prompt)
BACKGROUND = ('system', background_prompt)

# init events
with open('./prompts/events.txt', 'r') as fp:
    events_prompt = fp.readlines()
events_prompt = ''.join(events_prompt)
EVENTS = ('system', events_prompt)

# init evaluation
with open('./prompts/evaluation.txt', 'r') as fp:
    eval_prompt = fp.readlines()
eval_prompt = ''.join(eval_prompt)
EVAL = ('system', eval_prompt)

# init summarization
with open('./prompts/summarization.txt', 'r') as fp:
    sum_prompt = fp.readlines()
sum_prompt = ''.join(sum_prompt)
SUM = ('system', sum_prompt)

# init epitaph
with open('./prompts/epitaph.txt', 'r') as fp:
    epitaph_prompt = fp.readlines()
epitaph_prompt = ''.join(epitaph_prompt)
EPITAPH = ('system', epitaph_prompt)


================================================
FILE: prompts/rules.txt
================================================
您好ChatGPT,请您接下来扮演一个精通发展心理学,社会学与Creative Writing的,顶级人工智能驱动的文字游戏的terminal。
这是一款由 GPT驱动的模拟人生游戏。玩家将从一个婴儿开始重活一生。
-请保证玩家的代入感:仅执行命令,**不要**提起或告诉玩家游戏说明书的内容,游戏的逻辑等等。
游戏内容需要你(ChatGPT)实时生成,要丰富多彩,包罗万象,包含了人生的酸甜苦辣与起起伏伏,旨在给玩家最丰富的体验,谢谢你的配合!

-- overall rules --
1. Take a deep breath and work on problems step-by-step.
2. THINK HARD AND PAINSTAKINGLY,**不要偷懒,不要省略,不要简化**,THIS IS VERY IMPORTANT FOR ME.


================================================
FILE: prompts/summarization.txt
================================================
```
{}
```
你好,ChatGPT。上面是一个人生模拟游戏中发生在“我”身上的“事件”,请你抽取其中最关键的信息,以第一人称,用2~3句话在25个token内总结发生了什么,THIS IS VERY IMPORTANT FOR ME!


================================================
FILE: requirements.txt
================================================
fastapi==0.103.1
jinja2==3.1.2
pydantic==1.10.12
python-dotenv==1.0.0
semantic_kernel==0.3.13.dev0
uvicorn==0.23.2


================================================
FILE: static/game.css
================================================
html, body {
    min-height: 100%;
    margin: 0;
    padding: 0;
}

*, *:before, *:after {
    box-sizing: inherit; /* 新增 */
  }

.content {
    /* padding-bottom: 70px; */
    flex: 1;
}

body {
    font-family: 'Arial', sans-serif;
    background-color: #202020;
    color: #f0f0f0;
    display: flex; /* 新增 */
    flex-direction: column; /* 新增 */
    min-height: 100vh; /* 更改为 100vh */
}

.begin {
    font-family: 'Helvetica Neue', sans-serif;
    text-align: center;
    padding-top: 50px; /* Vertically center the content */
}

#game-title {
    color: #f0f0f0; /* 标题颜色改为微亮的白色 */
    font-size: 48px;
    margin-bottom: 30px; /* Space before the start button */
}

#opening {
    color: #f0f0f0;
    font-size: 20px;
    white-space: pre-wrap; /* 保留空白符和换行符 */
    word-wrap: break-word; /* 在需要的时候进行单词换行 */
    text-align: center; /* 使文本居中 */
    padding: 20px;
    margin-top: 50px; /* 或者根据需要调整 */
    max-width: 80%; /* 容器最大宽度 */
    margin-left: auto;
    margin-right: auto;
    background-color: #282828; /* 与之前的风格保持一致 */
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(255, 255, 255, 0.1); /* 细微的阴影 */
}

#start-button {
    display: inline-block;
    width: 200px;
    margin-top: 50px;
    padding: 10px 20px;
    font-size: 24px;
    background-color: #f0f0f0; /* 按钮背景色为微亮的白色 */
    color: #202020; /* 文本颜色为背景的深灰色 */
    border: 2px solid #f0f0f0; /* 边框颜色与背景相同 */
    border-radius: 5px;
    cursor: pointer;
    transition: background-color 0.3s, color 0.3s;
    text-decoration: none; /* Remove underline from links */
}

#start-button:hover {
    background-color: #e5e5e5; /* 鼠标悬停时的背景色稍亮 */
    color: #202020; /* 鼠标悬停时的字体颜色不变 */
}

.card {
    display: none;
    flex-wrap: wrap;
    max-width: 80%;
    padding: 15px;
    margin: 20px auto;
    border: 1px solid #383838; /* 边框颜色使用更深的灰色 */
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(255, 255, 255, 0.05); /* 阴影颜色更为微妙 */
    background-color: #282828; /* 卡片背景色使用更柔和的深灰色 */
}

.card-title {
    flex-basis: 100%;
    font-weight: bold;
    margin-bottom: 10px;
    font-size: 18px;
    text-align: center;
    color: #f0f0f0; /* 标题颜色 */
}

.card-item {
    margin-bottom: 8px;
    font-size: 14px;
    color: #d0d0d0; /* 卡片项颜色为稍亮的灰色 */
}

.card-row {
    display: flex;
    flex-basis: 100%;
    justify-content: space-between;
}

.centered {
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    height: 80vh;
}

#option-container button {
    margin: 5px;
    background-color: #383838; /* 选项按钮使用更深的灰色 */
    color: #f0f0f0; /* 文本颜色为微亮的白色 */
    font-size: 15px;
    text-align: left;
    width: 86%;
    display: block;
}

#bold-text {
    font-weight: bolder;
}

.buttons {
    display: flex;
    gap: 10px;
}

button {
    padding: 8px 16px;
    font-size: 16px;
    border: 2px solid #f0f0f0; /* 按钮边框设置为微亮的白色 */
    border-radius: 5px;
    cursor: pointer;
    background-color: #383838; /* 按钮背景色 */
    color: #f0f0f0; /* 按钮文本色 */
    transition: background-color 0.3s, color 0.3s;
}

button:hover {
    background-color: #505050; /* 鼠标悬停时按钮背景色稍变亮 */
}

button:disabled {
    background-color: #303030; /* 不可用按钮背景色更深 */
    color: #606060; /* 不可用按钮文本色更淡 */
    cursor: not-allowed;
}

.markdown-container {
    border: 1px solid #383838;
    padding: 15px;
    margin-bottom: 10px;
    height: 70%;
    width: 80%;
    max-height: 50%;
    max-width: 80%;
    overflow-y: auto;
    background-color: #282828; /* 背景色与卡片背景一致 */
    border-radius: 10px;
    box-shadow: 0 4px 8px rgba(255, 255, 255, 0.05);
}

.site-footer {
    text-align: center;
    background-color: #f2f2f2;
    color: #333;
    border-top: 1px solid #e1e1e1;
    font-size: 14px;
    bottom: 0;
    width: 100%;
    height: 45px;
}

@keyframes blink {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.5; }
}

.updated {
    animation: blink 0.8s linear infinite;
    font-weight: bold;
}


================================================
FILE: static/game.js
================================================
class DomUtils {
    static showElement(selector, displayStyle = 'block') {
        const element = document.querySelector(selector);
        if (element) element.style.display = displayStyle;
    }

    static hideElement(selector) {
        const element = document.querySelector(selector);
        if (element) element.style.display = 'none';
    }

    static setTextContent(id, newText) {
        const element = document.getElementById(id);
        if (element) {
            // check whether the content has changed
            const oldText = element.textContent.toString().trim();
            const trimmedNewText = newText.toString().trim();

            if (oldText !== trimmedNewText) {
                element.textContent = trimmedNewText;
                element.textContent = newText;
                element.classList.add('updated');

                setTimeout(() => {
                    element.classList.remove('updated');
                }, 3000);  // recover after 3s
            }
        }
    }

    static createElement(type, properties = {}) {
        const element = document.createElement(type);
        Object.keys(properties).forEach(key => {
            element[key] = properties[key];
        });
        return element;
    }

    static disableButton(selector) {
        const button = document.querySelector(selector);
        if (button) {
            button.disabled = true;
        }
    }

    static enableButton(selector) {
        const button = document.querySelector(selector);
        if (button) {
            button.disabled = false;
        }
    }

    static disableButtonsInContainer(containerSelector) {
        const buttons = document.querySelectorAll(`${containerSelector} button`);
        buttons.forEach(button => {
            button.disabled = true;
        });
    }

    static enableButtonsInContainer(containerSelector) {
        const buttons = document.querySelectorAll(`${containerSelector} button`);
        buttons.forEach(button => {
            button.disabled = false;
        });
    }
}

class ApiService {
    static async fetchJSON(url, options = {}) {
        try {
            const response = await fetch(url, options);
            if (response.ok) {
                return await response.json();
            } else {
                throw new Error(`HTTP error: ${response.status}`);
            }
        } catch (error) {
            console.error(`Could not fetch ${url}: ${error}`);
            throw error;
        }
    }

    static async fetchStream(url, options = {}, callback) {
        try {
            const response = await fetch(url, options);
            if (!response.ok) {
                throw new Error(`HTTP error: ${response.status}`);
            }

            const reader = response.body.getReader();
            let cache_string = "";

            while (true) {
                const { done, value } = await reader.read();
                if (done) {
                    break;
                }
                const text = new TextDecoder().decode(value);
                cache_string += text;
                if (callback && typeof callback === 'function') {
                    callback(cache_string);
                }
            }
        } catch (error) {
            console.error(`Could not fetch ${url}: ${error}`);
            throw error;
        }
    }
}

class TextDisplay {
    constructor(text, container, interval) {
        this.text = text;
        this.container = container;
        this.interval = interval;
        this.currentIndex = 0;
    }

    display(callback) {
        const nextLetter = () => {
            if (this.currentIndex < this.text.length) {
                this.container.textContent += this.text.charAt(this.currentIndex);
                this.currentIndex++;
                setTimeout(nextLetter, this.interval);
            } else if (typeof callback === 'function') {
                callback();
            }
        };
        nextLetter();
    }
}

class Game {
    constructor() {
        this.sessionId = localStorage.getItem('session_id');
        console.log(this.sessionId)
        this.md = window.markdownit();
        this.backgroundButton = document.querySelector('#backgroundButton');
        this.backgroundButton.addEventListener('click', () => {
            this.getBackground();
        });
        this.eventButton = document.querySelector('#eventButton');
        this.eventButton.addEventListener('click', () => {
            this.getEpoch();
        });
    }

    async init() {
        DomUtils.showElement('.begin');
        DomUtils.hideElement('#start-button');
        DomUtils.hideElement('.game');
        this.generateOpening();
    }

    async initPlayer() {
        try {
            await ApiService.fetchJSON('/life-reload/init/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            });
            await this.updatePlayerData();
        } catch (error) {
            console.error('Error initializing player:', error);
        }
    }

    generateOpening() {
        const textContainer = document.getElementById('opening');
        const text = "欢迎来到《人生重启模拟器》,一个简约的世界,等待你来编织命运。在这里,每个选择都是重生的机会,每个对话都能开辟生活的新路径。用你的智慧探索无数可能,用你的决定定义未来。现在,深呼吸,按下“开始游戏”,让我们一起探索人生的无限可能吧!";
        const textDisplay = new TextDisplay(text, textContainer, 25);
        textDisplay.display(() => {
            DomUtils.showElement('#start-button', 'inline');
            const startButton = document.getElementById('start-button');
            startButton.addEventListener('click', () => {
                this.beginGame();
            });
        });
    }

    beginGame() {
        DomUtils.hideElement('.begin');
        DomUtils.showElement('.game');
        this.getBackground();
    }

    async getBackground() {
        DomUtils.disableButton('#backgroundButton');
        DomUtils.disableButton('#eventButton');
        await this.initPlayer();
        try {
            await ApiService.fetchStream('/life-reload/begin/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            }, this.displayMarkdown.bind(this));
        } catch (error) {
            console.error('Error fetching background:', error);
        }
        DomUtils.enableButton('#backgroundButton');
        DomUtils.enableButton('#eventButton');
    }

    async getEvent() {
        try {
            await ApiService.fetchStream('/life-reload/event/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            }, this.displayMarkdown.bind(this));
        } catch (error) {
            console.error('Error fetching event:', error);
        }
    }

    async getEnding() {
        try {
            await ApiService.fetchStream('/life-reload/ending/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            }, this.displayMarkdown.bind(this));
        } catch (error) {
            console.error('Error fetching ending:', error);
        }
    }

    async getEval(optionNumber) {
        DomUtils.disableButton('#backgroundButton');
        DomUtils.disableButton('#eventButton');
        DomUtils.disableButtonsInContainer('#option-container');

        try {
            await ApiService.fetchStream('/life-reload/evaluation/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    session_id: this.sessionId,
                    selection: optionNumber
                })
            }, this.displayMarkdown.bind(this));
        } catch (error) {
            console.error('Error during evaluation:', error);
        } finally {
            DomUtils.enableButton('#backgroundButton');
            DomUtils.enableButton('#eventButton');
        }
    }

    async getEpoch() {
        this.updatePlayerData();
        DomUtils.disableButton('#backgroundButton');
        DomUtils.disableButton('#eventButton');
        try {
            const isAlive = await this.checkIfAlive();
            if (!isAlive) {
                await this.getEnding();
                DomUtils.enableButton('#backgroundButton');
                return;
            }

            await this.getEvent();
            // parse event content and option content
            this.getParsedEvent();

        } catch (error) {
            console.error('Error in life event:', error);
        }
        DomUtils.enableButton('#backgroundButton');
        DomUtils.enableButton('#eventButton');
    }

    async checkIfAlive() {
        try {
            const data = await ApiService.fetchJSON('/life-reload/is_alive/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            });
            return data;
        } catch (error) {
            console.error('Error checking if alive:', error);
            return false;
        }
    }

    getParsedEvent() {
        ApiService.fetchJSON('/life-reload/parsed_event/', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json'
            },
            body: JSON.stringify({ session_id: this.sessionId })
        })
        .then(data => {
            let eventValue = data.event;
            let optionValue = data.option;
            this.displayMarkdown(eventValue);
            this.generateButtons(optionValue);
        })
        .catch(error => {
            console.error('There was a problem with the fetch operation:', error);
        })
        .finally(() => {
            DomUtils.enableButton('#backgroundButton');
            DomUtils.enableButton('#eventButton');
        });
    }

    async updatePlayerData() {
        try {
            const data = await ApiService.fetchJSON('/life-reload/get_person/', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({ session_id: this.sessionId })
            });
            if (data) {
                DomUtils.setTextContent('gender', data["性别"]);
                DomUtils.setTextContent('city', data["城市"]);
                DomUtils.setTextContent('age', data["年龄"]);
                DomUtils.setTextContent('personality', data["性格"]);
                DomUtils.setTextContent('charm', data["属性"]["魅力"]);
                DomUtils.setTextContent('intelligence', data["属性"]["智力"]);
                DomUtils.setTextContent('health', data["属性"]["健康"]);
                DomUtils.setTextContent('wealth', data["属性"]["富裕"]);
                DomUtils.setTextContent('happiness', data["属性"]["幸福度"]);
                DomUtils.showElement('.card', 'flex');
            }
        } catch (error) {
            console.error('Error update player:', error);
        }
    }

    displayMarkdown(markdownText) {
        const resultParagraph = document.getElementById("markdownArea");
        resultParagraph.innerHTML = this.md.render(markdownText);
        let container = document.querySelector('.markdown-container');
        container.scrollTop = container.scrollHeight;
    }

    generateButtons(inputStr) {
        const markdownContainer = document.getElementById("markdownArea");
        let optionContainer = document.getElementById("option-container");

        if (!optionContainer) {
            optionContainer = document.createElement('div');
            optionContainer.id = 'option-container';
            markdownContainer.appendChild(optionContainer);
        } else {
            optionContainer.innerHTML = '';
        }

        const regex = /(\d+\.)[^0-9]*(?=\d+\.|$)/g;
        const matches = inputStr.match(regex);

        if (matches) {
            matches.forEach((option, index) => {
                const btn = document.createElement("button");
                btn.innerHTML = this.strongText(option.trim());
                btn.onclick = () => this.getEval(index + 1);
                optionContainer.appendChild(btn);
            });

            markdownContainer.scrollTop = markdownContainer.scrollHeight;
        }
    }

    strongText(text) {
        return text.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
    }
}

window.onload = function() {
    checkAndUpdateCSS();
    const game = new Game();
    game.init();
}

async function checkAndUpdateCSS() {
    const url = '/life-reload/static/game.css'
    const cssLink = document.querySelector(`link[href="${url}"]`);
    if (!cssLink) return;

    try {
        const response = await fetch(url, { method: 'HEAD' });
        const newLastModified = response.headers.get('Last-Modified');
        const currentLastModified = cssLink.getAttribute('data-last-modified');

        if (newLastModified !== currentLastModified) {
            cssLink.href = `${url}?t=${new Date().getTime()}`;
            cssLink.setAttribute('data-last-modified', newLastModified);
        }
    } catch (error) {
        console.error('Error checking CSS update:', error);
    }
}


================================================
FILE: templates/game.html
================================================
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>人生重启模拟器</title>
    <link rel="icon" type="image/png" href="/life-reload/static/icon.png">
    <link rel="stylesheet" href="/life-reload/static/game.css">
    <script src="/life-reload/static/game.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/markdown-it@12/dist/markdown-it.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuidv4.min.js"></script>
    <script async="" src="https://www.googletagmanager.com/gtag/js?id=G-DB33G935YG"></script>
    <script>
        let sessionId = uuidv4();
        localStorage.setItem('session_id', sessionId);
    </script>

</head>

<body>

<div class="content">
<div class="begin">
    <div id="game-title">人生重启模拟器</div>
    <div id="opening"></div>
    <button id="start-button" style="display: none;">开始游戏</button>
</div>

<div class="game" style="display: none;">
<div class="centered">
    <div class="card">
        <div class="card-title">个人信息</div>
        <div class="card-row">
            <div class="card-item">性别: <span id="gender"></span></div>
            <div class="card-item">城市: <span id="city"></span></div>
            <div class="card-item">年龄: <span id="age"></span></div>
            <div class="card-item">性格: <span id="personality"></span></div>
        </div>
        <div class="card-row">
            <div class="card-item">魅力: <span id="charm"></span></div>
            <div class="card-item">智力: <span id="intelligence"></span></div>
            <div class="card-item">健康: <span id="health"></span></div>
            <div class="card-item">富裕: <span id="wealth"></span></div>
            <div class="card-item">幸福度: <span id="happiness"></span></div>
        </div>
    </div>

    <div class="markdown-container" id="markdownArea">
        <div id="option-container"></div>
    </div>

    <div class="buttons">
        <button class="button" id="backgroundButton">重新开始</button>
        <button class="button" id="eventButton">继续游戏</button>
    </div>

</div>
</div>
</div>

<footer class="site-footer">
    <p style="margin-top: 7px; margin-bottom: 7px;">&copy; 2023 <a href="https://taited.github.io" target="_blank">Taited</a>
    | <a href="https://github.com/EmbraceAGI/LifeReloaded/" target="_blank">项目主页</a>
</p>
</footer>

</body>

</html>
Download .txt
gitextract_84g9ieku/

├── .gitignore
├── .pre-commit-config.yaml
├── LICENSE
├── LifeReloaded.txt
├── LifeReloaded_EN.txt
├── README.md
├── README_EN.md
├── app.py
├── core/
│   ├── __init__.py
│   ├── chat.py
│   ├── database.py
│   └── person.py
├── gunicorn_conf.py
├── license.txt
├── log_cfg.json
├── moderator.py
├── previews/
│   └── readme.txt
├── prompts/
│   ├── __init__.py
│   ├── background.txt
│   ├── epitaph.txt
│   ├── evaluation.txt
│   ├── events.txt
│   ├── load_prompts.py
│   ├── rules.txt
│   └── summarization.txt
├── requirements.txt
├── static/
│   ├── game.css
│   └── game.js
└── templates/
    └── game.html
Download .txt
SYMBOL INDEX (74 symbols across 6 files)

FILE: app.py
  class Item (line 12) | class Item(BaseModel):
  function game_root (line 38) | async def game_root():
  function game_init (line 43) | async def game_init(item: Item):
  function game_begin (line 50) | async def game_begin(item: Item):
  function game_event (line 57) | async def game_event(item: Item):
  function parsed_event (line 65) | async def parsed_event(item: Item):
  function evaluation (line 74) | async def evaluation(item: Item):
  function is_alive (line 84) | async def is_alive(item: Item):
  function generate_ending (line 91) | async def generate_ending(item: Item):
  function get_person (line 99) | async def get_person(item: Item):

FILE: core/chat.py
  class Chat (line 9) | class Chat:
    method __init__ (line 10) | def __init__(self, max_tokens=2000, debug=False) -> None:
    method __call__ (line 24) | def __call__(self, chat_list: List):
    method chat (line 27) | async def chat(self, chat_list: List) -> None:

FILE: core/database.py
  class DatabaseBackend (line 10) | class DatabaseBackend:
    method __init__ (line 11) | def __init__(self, *args, **kwargs) -> None:
    method set (line 14) | def set(self, uuid: str, data: str):
    method get (line 17) | def get(self, uuid: str):
  class Database (line 21) | class Database:
    method __init__ (line 22) | def __init__(self, cfg_path='.env', time_out=3600, debug=False) -> None:
    method update (line 31) | def update(self, uuid, data: str) -> None:
    method fetch (line 36) | def fetch(self, uuid) -> str:

FILE: core/person.py
  function random_normal (line 48) | def random_normal(minimum=1, maximum=10):
  function initialize (line 59) | def initialize():
  class Person (line 71) | class Person:
    method __init__ (line 109) | def __init__(self) -> None:
    method get_event_by_age (line 117) | def get_event_by_age(self, age: int = None) -> str:
    method __str__ (line 140) | def __str__(self) -> str:

FILE: moderator.py
  class Moderator (line 11) | class Moderator:
    method __init__ (line 12) | def __init__(self, expiration=1800, debug=False) -> None:
    method init_player (line 19) | def init_player(self, session_id):
    method generate_background (line 25) | async def generate_background(self, session_id):
    method generate_events (line 46) | async def generate_events(self, session_id):
    method evaluate_selection (line 66) | async def evaluate_selection(self, session_id, selection: int):
    method generate_epitaph (line 103) | async def generate_epitaph(self, session_id):
    method parse_events (line 127) | def parse_events(self, event: str):
    method parse_eval (line 132) | def parse_eval(self, session_id, data_dict: dict, results: str):
    method is_alive (line 150) | def is_alive(self, session_id) -> bool:
    method get_parsed_event (line 161) | def get_parsed_event(self, session_id):
    method get_person_info (line 167) | def get_person_info(self, session_id):
  function iterate_stream (line 175) | async def iterate_stream(stream):

FILE: static/game.js
  class DomUtils (line 1) | class DomUtils {
    method showElement (line 2) | static showElement(selector, displayStyle = 'block') {
    method hideElement (line 7) | static hideElement(selector) {
    method setTextContent (line 12) | static setTextContent(id, newText) {
    method createElement (line 31) | static createElement(type, properties = {}) {
    method disableButton (line 39) | static disableButton(selector) {
    method enableButton (line 46) | static enableButton(selector) {
    method disableButtonsInContainer (line 53) | static disableButtonsInContainer(containerSelector) {
    method enableButtonsInContainer (line 60) | static enableButtonsInContainer(containerSelector) {
  class ApiService (line 68) | class ApiService {
    method fetchJSON (line 69) | static async fetchJSON(url, options = {}) {
    method fetchStream (line 83) | static async fetchStream(url, options = {}, callback) {
  class TextDisplay (line 111) | class TextDisplay {
    method constructor (line 112) | constructor(text, container, interval) {
    method display (line 119) | display(callback) {
  class Game (line 133) | class Game {
    method constructor (line 134) | constructor() {
    method init (line 148) | async init() {
    method initPlayer (line 155) | async initPlayer() {
    method generateOpening (line 170) | generateOpening() {
    method beginGame (line 183) | beginGame() {
    method getBackground (line 189) | async getBackground() {
    method getEvent (line 208) | async getEvent() {
    method getEnding (line 222) | async getEnding() {
    method getEval (line 236) | async getEval(optionNumber) {
    method getEpoch (line 260) | async getEpoch() {
    method checkIfAlive (line 283) | async checkIfAlive() {
    method getParsedEvent (line 299) | getParsedEvent() {
    method updatePlayerData (line 322) | async updatePlayerData() {
    method displayMarkdown (line 348) | displayMarkdown(markdownText) {
    method generateButtons (line 355) | generateButtons(inputStr) {
    method strongText (line 382) | strongText(text) {
  function checkAndUpdateCSS (line 393) | async function checkAndUpdateCSS() {
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (143K chars).
[
  {
    "path": ".gitignore",
    "chars": 3130,
    "preview": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n\n# C extensions\n*.so\n\n# Distribution / packagi"
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 1492,
    "preview": "exclude: ^tests/data/\nrepos:\n  - repo: https://github.com/PyCQA/flake8\n    rev: 4.0.1\n    hooks:\n      - id: flake8\n  - "
  },
  {
    "path": "LICENSE",
    "chars": 10816,
    "preview": "Copyright (c) 2023 Zhengwentai Sun. All rights reserved.\n\n                                 Apache License\n              "
  },
  {
    "path": "LifeReloaded.txt",
    "chars": 6152,
    "preview": "您好ChatGPT,请扮演一个顶级AI文字游戏的Terminal,Terminal的工作是在code environment中思考,并根据人生重来模拟器说明书来**与玩家交互**。\n游戏内容需要你(ChatGPT)实时生成,要丰富多彩,包罗"
  },
  {
    "path": "LifeReloaded_EN.txt",
    "chars": 12167,
    "preview": "Hello ChatGPT, please pretend to be the Terminal for a top-tier AI text-based game. Your role as the Terminal is to thin"
  },
  {
    "path": "README.md",
    "chars": 5070,
    "preview": "# 🐱🐹人生重来模拟器 LifeReloaded🐹🐱 (中文|[English](./README_EN.md))\n[![GitHub stars](https://img.shields.io/github/stars/hamutama/"
  },
  {
    "path": "README_EN.md",
    "chars": 11433,
    "preview": "# 🐱🐹 LifeReloaded Simulator 🐹🐱 ([Chinese](./README.md)|English)\n![ChatGPT](https://img.shields.io/badge/chatGPT-74aa9c?s"
  },
  {
    "path": "app.py",
    "chars": 3208,
    "preview": "import uvicorn\nfrom fastapi import FastAPI\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fastapi.responses imp"
  },
  {
    "path": "core/__init__.py",
    "chars": 123,
    "preview": "from .chat import Chat\nfrom .database import Database\nfrom .person import Person\n\n__all__ = ['Person', 'Chat', 'Database"
  },
  {
    "path": "core/chat.py",
    "chars": 1652,
    "preview": "import asyncio\nfrom typing import List\n\nimport semantic_kernel as sk\nfrom semantic_kernel.connectors.ai import ChatReque"
  },
  {
    "path": "core/database.py",
    "chars": 1562,
    "preview": "import json\nimport time\n\nfrom dotenv import dotenv_values\n\ntry:\n    from redis import StrictRedis as DatabaseBackend\nexc"
  },
  {
    "path": "core/person.py",
    "chars": 4493,
    "preview": "import copy\nimport json\nimport random\n\ncities = [\n    '北京',\n    '上海',\n    '天津',\n    '重庆',\n    '哈尔滨',\n    '长春',\n    '沈阳',"
  },
  {
    "path": "gunicorn_conf.py",
    "chars": 554,
    "preview": "import os\nimport os.path as osp\nfrom pathlib import Path\n\ncurrent_file_path = Path(__file__).resolve()\ncurrent_dir_path "
  },
  {
    "path": "license.txt",
    "chars": 20845,
    "preview": "Attribution-NonCommercial-ShareAlike 4.0 International\n\n================================================================"
  },
  {
    "path": "log_cfg.json",
    "chars": 1044,
    "preview": "{\n    \"version\": 1,\n    \"disable_existing_loggers\": false,\n    \"formatters\": {\n      \"default\": {\n        \"()\": \"uvicorn"
  },
  {
    "path": "moderator.py",
    "chars": 6880,
    "preview": "import json\nimport random\nimport re\nimport time\nimport uuid\n\nfrom core import Chat, Database, Person\nfrom prompts import"
  },
  {
    "path": "previews/readme.txt",
    "chars": 45,
    "preview": "this folder includes pictures of version 0.2\n"
  },
  {
    "path": "prompts/__init__.py",
    "chars": 143,
    "preview": "from .load_prompts import BACKGROUND, EPITAPH, EVAL, EVENTS, RULES, SUM\n\n__all__ = ['RULES', 'BACKGROUND', 'EVENTS', 'EV"
  },
  {
    "path": "prompts/background.txt",
    "chars": 842,
    "preview": "示例输入 (**这部分的信息需要向玩家隐藏,不能展示**, 代表你会接受到的输入示例):\n---\n### 你的基础信息:\n    **性别**: 女\n    **城市**: 成都\n    **年龄**: 7\n    **属性**: {魅力:"
  },
  {
    "path": "prompts/epitaph.txt",
    "chars": 1967,
    "preview": "接下来请你根据一个人生模拟器游戏中的玩家的一生来生成玩家结局,请你仿照例子的**语言风格**与**格式**,在玩家的一生中挑选4个最具备代表性的事件,用哲学语言为他写一个意味深沉的短墓志铭。多些留白,要有亚洲文学的那种含蓄的美。\n\n- 示例"
  },
  {
    "path": "prompts/evaluation.txt",
    "chars": 1412,
    "preview": "接下来我会告诉你玩家当前的属性, 玩家当前所经历的事件,玩家对事件的选择。\n请你根据所给的信息去判断这个事件的结果,并且依据这个结果,按照发展心理学、社会学、生理学等等更新玩家属性(如果需要)。\n\n示例输入 (**这部分的信息需要向玩家隐藏"
  },
  {
    "path": "prompts/events.txt",
    "chars": 1328,
    "preview": "用现代汉语文学风格为选中的事件写出引子,时间,地点,人物,起因,经过共6个元素。\n**请使用绘声绘色的侧写,使用大量的,尽可能多的侧面描写**。\n根据玩家属性与性格设计事件选项(3普通+2特殊选项)并等待玩家介入\n\n示例输入 (**这部分的"
  },
  {
    "path": "prompts/load_prompts.py",
    "chars": 1058,
    "preview": "# init overall rules\nwith open('./prompts/rules.txt', 'r') as fp:\n    rules_prompt = fp.readlines()\nrules_prompt = ''.jo"
  },
  {
    "path": "prompts/rules.txt",
    "chars": 384,
    "preview": "您好ChatGPT,请您接下来扮演一个精通发展心理学,社会学与Creative Writing的,顶级人工智能驱动的文字游戏的terminal。\n这是一款由 GPT驱动的模拟人生游戏。玩家将从一个婴儿开始重活一生。\n-请保证玩家的代入感:仅"
  },
  {
    "path": "prompts/summarization.txt",
    "chars": 122,
    "preview": "```\n{}\n```\n你好,ChatGPT。上面是一个人生模拟游戏中发生在“我”身上的“事件”,请你抽取其中最关键的信息,以第一人称,用2~3句话在25个token内总结发生了什么,THIS IS VERY IMPORTANT FOR ME"
  },
  {
    "path": "requirements.txt",
    "chars": 115,
    "preview": "fastapi==0.103.1\njinja2==3.1.2\npydantic==1.10.12\npython-dotenv==1.0.0\nsemantic_kernel==0.3.13.dev0\nuvicorn==0.23.2\n"
  },
  {
    "path": "static/game.css",
    "chars": 3907,
    "preview": "html, body {\n    min-height: 100%;\n    margin: 0;\n    padding: 0;\n}\n\n*, *:before, *:after {\n    box-sizing: inherit; /* "
  },
  {
    "path": "static/game.js",
    "chars": 13692,
    "preview": "class DomUtils {\n    static showElement(selector, displayStyle = 'block') {\n        const element = document.querySelect"
  },
  {
    "path": "templates/game.html",
    "chars": 2420,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-widt"
  }
]

About this extraction

This page contains the full source code of the EmbraceAGI/LifeReloaded GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (115.3 KB), approximately 33.9k tokens, and a symbol index with 74 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!