text:
# A workshop for rmarkdown

Content for RMarkdown workshop at SIPS 2017.

`handout.Rmd` is the tutorial content. You can find the HTML version [here](https://libscie.github.io/rmarkdown-workshop/handout.html) or the PDF version [here](https://libscie.github.io/rmarkdown-workshop/handout.pdf)

You'll need [RStudio](http://www.rstudio.com) for this workshop. Also, to get the full APA functionality working, you'll need to install `papaja` and follow all the texlive installation instructions on the [papaja page](https://github.com/crsh/papaja).


text:
[color_theme]

background_color="2e3440ff"
base_type_color="8fbcbbff"
bookmark_color="147dfaff"
brace_mismatch_color="ff786bff"
breakpoint_color="ff786bff"
caret_background_color="000000ff"
caret_color="ffffffff"
code_folding_color="ffffffb3"
comment_color="63708aff"
completion_background_color="333b4fff"
completion_existing_color="ffffff24"
completion_font_color="ccced3ff"
completion_scroll_color="ffffff12"
completion_selected_color="ffffff12"
current_line_color="434c5e6a"
engine_type_color="8fbcbbff"
executing_line_color="33cc3366"
function_color="88c0d0ff"
gdscript/function_definition_color="88c0d0ff"
gdscript/node_path_color="ebcb8bff"
keyword_color="81a1c1ff"
line_length_guideline_color="4d576b80"
line_number_color="4c566aff"
mark_color="bf626b60"
member_variable_color="d8dee9ff"
number_color="b48eadff"
safe_line_number_color="507358ff"
search_result_border_color="699ce861"
search_result_color="ffffff12"
selection_color="434c5eff"
string_color="a3be8cff"
symbol_color="81a1c1ff"
text_color="eceff4ff"
text_selected_color="1a1d28ff"
user_type_color="8fbcbbff"
word_highlighted_color="4c566aff"

repo_name: godotengine/godot-syntax-themes

mime_type: text/plain


text:
[color_theme]

background_color="002b36ff"
completion_background_color="3d3d3dff"
completion_selected_color="ffffff11"
completion_existing_color="ffffff23"
completion_scroll_color="ffffff11"
completion_font_color="cececeff"
caret_color="ffffffff"
caret_background_color="000000ff"
line_number_color="586e75ff"
text_color="839496ff"
text_selected_color="eee8d5ff"
keyword_color="b58900ff"
base_type_color="dc322fff"
engine_type_color="d33682ff"
function_color="268bd2ff"
member_variable_color="268bd2ff"
comment_color="586e75ff"
string_color="2aa198ff"
number_color="d33682ff"
symbol_color="839496ff"
selection_color="274642ff"
brace_mismatch_color="ff3333ff"
current_line_color="073642ff"
line_length_guideline_color="103b46ff"
mark_color="ff323238"
breakpoint_color="ff4d56ff"
code_folding_color="808080ff"
word_highlighted_color="004a5cff"
search_result_color="4d3b24ff"
search_result_border_color="00000000"
gdscript/function_definition_color="2aa198ff"
gdscript/node_path_color="d33682ff"


text:
[color_theme]

background_color="242424ff"
completion_background_color="343434ff"
completion_selected_color="464646ff"
completion_existing_color="dfdfdf21"
completion_scroll_color="ffffffff"
completion_font_color="aaaaaaff"
caret_color="aaaaaaff"
caret_background_color="2c2c2cff"
line_number_color="666666ff"
text_color="ccccccff"
text_selected_color="ccccccff"
keyword_color="cc8242ff"
base_type_color="a5c261ff"
engine_type_color="ffc66dff"
function_color="ffc66dff"
member_variable_color="9e7bb0ff"
comment_color="707070ff"
string_color="6a8759ff"
number_color="7a9ec2ff"
symbol_color="ccccccff"
selection_color="00c3ff30"
brace_mismatch_color="ff3333ff"
current_line_color="ffffff0b"
line_length_guideline_color="333333ff"
mark_color="ff323238"
breakpoint_color="cccc6633"
code_folding_color="808080ff"
word_highlighted_color="00c2ff14"
search_result_color="653618ff"
search_result_border_color="00000000"
gdscript/function_definition_color="ffc66dff"
gdscript/node_path_color="bbb529ff"


text:
[color_theme]

background_color="282c34ff"
completion_background_color="21252bff"
completion_selected_color="2c313aff"
completion_existing_color="314365ff"
completion_scroll_color="2c313aff"
completion_font_color="abb2bfff"
caret_color="f8f8f0ff"
caret_background_color="000000ff"
line_number_color="495162ff"
safe_line_number_color="808080ff"
text_color="abb2bfff"
text_selected_color="abb2bfff"
keyword_color="e06c75ff"
base_type_color="d19a66ff"
engine_type_color="61afefff"
user_type_color="56b6c2ff"
function_color="98c379ff"
member_variable_color="61afefff"
comment_color="5c6370ff"
string_color="e5c07bff"
number_color="c678ddff"
symbol_color="808080ff"
selection_color="3e4451ff"
brace_mismatch_color="c24038ff"
current_line_color="383e4aff"
line_length_guideline_color="3b4048ff"
mark_color="ff323238"
breakpoint_color="c24038ff"
code_folding_color="808080ff"
word_highlighted_color="3e4141ff"
search_result_color="314365ff"
search_result_border_color="00000000"
gdscript/function_definition_color="98c379ff"
gdscript/node_path_color="d19a66ff"


text:
[color_theme]

background_color="282828ff"
completion_background_color="3d3d3dff"
completion_selected_color="ffffff11"
completion_existing_color="ffffff23"
completion_scroll_color="ffffff11"
completion_font_color="cececeff"
caret_color="a89984ff"
caret_background_color="000000ff"
line_number_color="928374ff"
text_color="fbf1c7ff"
text_selected_color="ebdbb2ff"
keyword_color="fb4934ff"
base_type_color="fabd2fff"
engine_type_color="83a598ff"
function_color="8ec07cff"
member_variable_color="e64e59ff"
comment_color="928374ff"
string_color="b8bb26ff"
number_color="d3869bff"
symbol_color="fbf1c7ff"
selection_color="504945ff"
brace_mismatch_color="ff3333ff"
current_line_color="3c3836ff"
line_length_guideline_color="383838ff"
mark_color="ff323238"
breakpoint_color="ff4d56ff"
code_folding_color="808080ff"
word_highlighted_color="2f2c2bff"
search_result_color="68391bff"
search_result_border_color="ffffff00"
gdscript/function_definition_color="b8bb26ff"
gdscript/node_path_color="d3869bff"


text:
[color_theme]

symbol_color="bcbcbcff"
keyword_color="8fafd7ff"

base_type_color="8787afff"
engine_type_color="8787afff"
user_type_color="8787afff"

comment_color="6c6c6cff"
string_color="87af87ff"
background_color="262626ff"

completion_background_color="444444ff"
completion_selected_color="6c6c6cff"
completion_existing_color="5fafaf59"
completion_scroll_color="6c6c6cff"
completion_font_color="bcbcbcff"

text_color="bcbcbcff"
line_number_color="444444ff"
safe_line_number_color="5f875f59"

caret_color="5fafafff"
caret_background_color="444444ff"

text_selected_color="ccccccff"
selection_color="00c3ff30"
brace_mismatch_color="ff3333ff"
current_line_color="ffffff0b"
line_length_guideline_color="444444ff"

word_highlighted_color="ffffff1a"

number_color="ffffafff"
function_color="ffffffff"
member_variable_color="bcbcbcff"

mark_color="ff323238"
bookmark_color="87af87ff"
breakpoint_color="ff8700ff"
code_folding_color="6c6c6cff"

search_result_color="5fafaf59"
search_result_border_color="00000000"

gdscript/function_definition_color="ffffafff"
gdscript/node_path_color="5fafafff"


text:
[color_theme]

background_color="25252aff"
completion_background_color="2c2a32ff"
completion_selected_color="434244ff"
completion_existing_color="dfdfdf21"
completion_scroll_color="ffffffff"
completion_font_color="aaaaaaff"
caret_color="ffffffff"
caret_background_color="000000ff"
line_number_color="aaaaaa66"
text_color="ffffffff"
text_selected_color="ffffffff"
keyword_color="668ad1ff"
base_type_color="4da941ff"
engine_type_color="cf7d34ff"
function_color="e36b48ff"
member_variable_color="d16060ff"
comment_color="767f8cff"
string_color="ffc35aff"
number_color="ff9292ff"
symbol_color="9b859dff"
selection_color="333539ff"
brace_mismatch_color="ff3333ff"
current_line_color="1e1e23ff"
line_length_guideline_color="35353aff"
mark_color="ff323238"
breakpoint_color="cccc6633"
code_folding_color="808080ff"
word_highlighted_color="cce5e526"
search_result_color="0c3f0cff"
search_result_border_color="197219ff"
gdscript/function_definition_color="ffc35aff"
gdscript/node_path_color="ff9292ff"


text:
[color_theme]

background_color="ffffffff"
base_type_color="af00dbff"
bookmark_color="147dfaff"
brace_mismatch_color="ff3333ff"
breakpoint_color="cccc6633"
caret_background_color="000000ff"
caret_color="000000ff"
code_folding_color="808080ff"
comment_color="008000ff"
completion_background_color="eeeeeeff"
completion_existing_color="dfdfdf21"
completion_font_color="333333ff"
completion_scroll_color="aaaaaaff"
completion_selected_color="ccccccff"
control_flow_keyword_color="a70edcff"
current_line_color="0000000c"
engine_type_color="267f99ff"
executing_line_color="33cc3366"
function_color="795e26ff"
gdscript/function_definition_color="795e26ff"
gdscript/node_path_color="2f6bbdff"
keyword_color="0000ffff"
line_length_guideline_color="eeeeeeff"
line_number_color="397591ff"
mark_color="ff323230"
member_variable_color="001080ff"
number_color="09885aff"
safe_line_number_color="77a35dff"
search_result_border_color="00000000"
search_result_color="f8c9abff"
selection_color="add6ffff"
string_color="a31515ff"
symbol_color="000000ff"
text_color="000000ff"
text_selected_color="000000ff"
user_type_color="09885aff"
word_highlighted_color="d9e9feff"


text:
[color_theme]

background_color="282828ff"
completion_background_color="3d3d3dff"
completion_selected_color="ffffff11"
completion_existing_color="ffffff23"
completion_scroll_color="ffffff11"
completion_font_color="cececeff"
caret_color="f8f8f0ff"
caret_background_color="000000ff"
line_number_color="aaaaaa66"
text_color="f8f8f2ff"
text_selected_color="f8f8f2ff"
keyword_color="f92672ff"
base_type_color="a6e22eff"
engine_type_color="ae81ffff"
function_color="66d9efff"
member_variable_color="66d9efff"
comment_color="75715eff"
string_color="e6db74ff"
number_color="ae81ffff"
symbol_color="f8f8f2ff"
selection_color="49483eff"
brace_mismatch_color="ff3333ff"
current_line_color="323232ff"
line_length_guideline_color="393939ff"
mark_color="ff323238"
breakpoint_color="ff4d56ff"
code_folding_color="808080ff"
word_highlighted_color="3e4141ff"
search_result_color="673917ff"
search_result_border_color="00000000"
gdscript/function_definition_color="a6e22eff"
gdscript/node_path_color="fd971fff"


text:
[color_theme]

background_color="282b34ff"
completion_background_color="3d3d3dff"
completion_selected_color="ffffff11"
completion_existing_color="ffffff23"
completion_scroll_color="ffffff11"
completion_font_color="cececeff"
caret_color="528affff"
caret_background_color="000000ff"
line_number_color="6a717eff"
text_color="aab1bfff"
text_selected_color="aab1bfff"
keyword_color="c677ddff"
base_type_color="e6bf7bff"
engine_type_color="56b6c1ff"
function_color="60aeedff"
member_variable_color="e06c75ff"
comment_color="6f6f6fff"
string_color="97c379ff"
number_color="d19a66ff"
symbol_color="aab1bfff"
selection_color="404859ff"
brace_mismatch_color="ff3333ff"
current_line_color="30353eff"
line_length_guideline_color="383b44ff"
mark_color="ff323238"
breakpoint_color="ff4d56ff"
code_folding_color="808080ff"
word_highlighted_color="353840ff"
search_result_color="314365ff"
search_result_border_color="4982ffff"
gdscript/function_definition_color="60aeedff"
gdscript/node_path_color="d19a66ff"


text:
# Theme previews

## Atom Dark

![Atom Dark](https://archive.hugo.pro/.public/godot-syntax-themes/atom-dark.png)

## Darcula

![Darcula](https://archive.hugo.pro/.public/godot-syntax-themes/darcula.png)

## Dracula

![Dracula](https://archive.hugo.pro/.public/godot-syntax-themes/dracula.png)

## Gruvbox Dark

![Gruvbox Dark](https://archive.hugo.pro/.public/godot-syntax-themes/gruvbox-dark.png)

## Metro

![Metro](https://archive.hugo.pro/.public/godot-syntax-themes/metro.png)

## Monokai

![Monokai](https://archive.hugo.pro/.public/godot-syntax-themes/monokai.png)

## One Dark

![One Dark](https://archive.hugo.pro/.public/godot-syntax-themes/one-dark.png)

## Quiet Light

![Quiet Light](https://archive.hugo.pro/.public/godot-syntax-themes/quiet-light.png)

## Solarized Dark

![Solarized Dark](https://archive.hugo.pro/.public/godot-syntax-themes/solarized-dark.png)

## Solarized Light

![Solarized Light](https://archive.hugo.pro/.public/godot-syntax-themes/solarized-light.png)

## VS Code Dark

![VS Code Dark](https://archive.hugo.pro/.public/godot-syntax-themes/vs-code-dark.png)

## VS Code Light

![VS Code Light](https://archive.hugo.pro/.public/godot-syntax-themes/vs-code-light.png)


text:
[color_theme]

background_color="1f2430ff"
base_type_color="5ce6ccff"
bookmark_color="73d0ffff"
brace_mismatch_color="ff786bff"
breakpoint_color="f28779ff"
caret_background_color="1f2430ff"
caret_color="ffcc66ff"
code_folding_color="ffffffb3"
comment_color="5c6773ff"
completion_background_color="333b4fff"
completion_existing_color="ffffff24"
completion_font_color="ccced3ff"
completion_scroll_color="ffffff12"
completion_selected_color="ffffff12"
current_line_color="0000002b"
engine_type_color="5ccfe6ff"
executing_line_color="33cc3366"
function_color="f28779ff"
gdscript/function_definition_color="f29e74ff"
gdscript/node_path_color="73b7ffff"
keyword_color="ffa759ff"
line_length_guideline_color="2e3547ff"
line_number_color="3f4654ff"
mark_color="80231b83"
member_variable_color="ffcc66ff"
number_color="ffcc66ff"
safe_line_number_color="33664dff"
search_result_border_color="ffcc66ff"
search_result_color="25272dff"
selection_color="2a3546ff"
string_color="bae67eff"
symbol_color="f29e74ff"
text_color="cbccc6ff"
text_selected_color="cbccc6ff"
user_type_color="73d0ffff"
word_highlighted_color="262f3eff"


text:
[color_theme]

background_color="282a36ff"
completion_background_color="2c2a32ff"
completion_selected_color="434244ff"
completion_existing_color="dfdfdf21"
completion_scroll_color="ffffffff"
completion_font_color="aaaaaaff"
caret_color="aeafadff"
caret_background_color="000000ff"
line_number_color="6272a4ff"
text_color="f8f8f2ff"
text_selected_color="f8f8f2ff"
keyword_color="ff79c6ff"
base_type_color="50fa7bff"
engine_type_color="8be9fdff"
function_color="8be9fdff"
member_variable_color="f8f8f2ff"
comment_color="6272a4ff"
string_color="f1fa8cff"
number_color="bd93f9ff"
symbol_color="f8f8f2ff"
selection_color="44475aff"
brace_mismatch_color="ff3333ff"
current_line_color="2f3245ff"
line_length_guideline_color="393c4fff"
mark_color="ff323238"
breakpoint_color="cccc6633"
code_folding_color="808080ff"
word_highlighted_color="424450ff"
search_result_color="5e6069ff"
search_result_border_color="00000000"
gdscript/function_definition_color="50fa7bff"
gdscript/node_path_color="ffb86cff"


text:
[color_theme]

background_color="f5f5f5ff"
completion_background_color="e5e5e5ff"
completion_selected_color="d5d5d5ff"
completion_existing_color="dfdfdf21"
completion_scroll_color="959595ff"
completion_font_color="454545ff"
caret_color="54494bff"
caret_background_color="000000ff"
line_number_color="9da39aff"
text_color="333333ff"
text_selected_color="333333ff"
keyword_color="4b83cdff"
base_type_color="aa3731ff"
engine_type_color="7a3e9dff"
function_color="aa3731ff"
member_variable_color="7a3e9dff"
comment_color="aaaaaaff"
string_color="448c27ff"
number_color="ab6526ff"
symbol_color="777777ff"
selection_color="c9d0d9ff"
brace_mismatch_color="ff3333ff"
current_line_color="e4f6d4ff"
line_length_guideline_color="e4e4e4ff"
mark_color="ff323230"
breakpoint_color="cccc6633"
code_folding_color="808080ff"
word_highlighted_color="e8ebeeff"
search_result_color="edc9d8ff"
search_result_border_color="00000000"
gdscript/function_definition_color="aa3731ff"
gdscript/node_path_color="ab6526ff"


text:
[color_theme]

background_color="1d1f21ff"
base_type_color="99cc99ff"
bookmark_color="147dfaff"
brace_mismatch_color="ff3333ff"
breakpoint_color="ff4d56ff"
caret_background_color="000000ff"
caret_color="ffffffff"
code_folding_color="808080ff"
comment_color="7c7c7cff"
completion_background_color="3d3d3dff"
completion_existing_color="ffffff23"
completion_font_color="cececeff"
completion_scroll_color="ffffff11"
completion_selected_color="ffffff11"
current_line_color="ffffff12"
engine_type_color="f9ee98ff"
executing_line_color="33cc3366"
function_color="dad085ff"
gdscript/function_definition_color="f9ee98ff"
gdscript/node_path_color="99cc99ff"
keyword_color="96cbfeff"
line_length_guideline_color="2d2f31ff"
line_number_color="656866ff"
mark_color="ff323238"
member_variable_color="c6c5feff"
number_color="ff73fdff"
safe_line_number_color="ccf7d3bf"
search_result_border_color="ffffffb2"
search_result_color="ffffff11"
selection_color="444444ff"
string_color="a8ff60ff"
symbol_color="c5c8c6ff"
text_color="c5c8c6ff"
text_selected_color="eeeeeeff"
user_type_color="c6ffedff"
word_highlighted_color="343434ff"


text:
[color_theme]

background_color="1C1E26ff"
completion_background_color="2E303E80"
completion_selected_color="6C6F9380"
completion_existing_color="D5D8DA20"
completion_scroll_color="232530ff"
completion_font_color="D5D8DAff"
caret_color="e95378ff"
caret_background_color="1C1E26ff"
line_number_color="D5D8DA80"
text_color="D5D8DAff"
text_selected_color="6C6F9380"
keyword_color="B877DBE6"
base_type_color="FAC29Aff"
engine_type_color="FAC29Aff"
user_type_color="FAC29Aff"
function_color="25B2BCff"
member_variable_color="E95678ff"
comment_color="D5D8DA80"
string_color="FAB795ff"
number_color="FAB795ff"
symbol_color="D5D8DAff"
selection_color="6C6F9380"
brace_mismatch_color="E95678B3"
current_line_color="1C1E2680"
line_length_guideline_color="ffffffff"
mark_color="E9567820"
breakpoint_color="ff0000ff"
code_folding_color="ffffffff"
word_highlighted_color="6C6F9330"
search_result_color="6C6F9380"
search_result_border_color="6C6F9380"
gdscript/function_definition_color="25B2BCff"
gdscript/node_path_color="F09383ff"


text:
[color_theme]

background_color="fdf6e3ff"
completion_background_color="ede6d3ff"
completion_selected_color="ddd6b3ff"
completion_existing_color="dfdfdf21"
completion_scroll_color="657b83ff"
completion_font_color="444444ff"
caret_color="657b83ff"
caret_background_color="000000ff"
line_number_color="2b91afff"
text_color="333333ff"
text_selected_color="333333ff"
keyword_color="859900ff"
base_type_color="073642ff"
engine_type_color="cb4b16ff"
function_color="268bd2ff"
member_variable_color="268bd2ff"
comment_color="93a1a1ff"
string_color="2aa198ff"
number_color="d33682ff"
symbol_color="333333ff"
selection_color="dad3c1ff"
brace_mismatch_color="ff3333ff"
current_line_color="f5eedcff"
line_length_guideline_color="e5deccff"
mark_color="ff323230"
breakpoint_color="cccc6633"
code_folding_color="808080ff"
word_highlighted_color="eee8d5ff"
search_result_color="f7c399ff"
search_result_border_color="00000000"
gdscript/function_definition_color="268bd2ff"
gdscript/node_path_color="cb4b16ff"


text:
[color_theme]

background_color="1e1e1eff"
base_type_color="c586c0ff"
bookmark_color="147dfaff"
brace_mismatch_color="ff3333ff"
breakpoint_color="cccc6633"
caret_background_color="000000ff"
caret_color="aeafadff"
code_folding_color="808080ff"
comment_color="608b4eff"
completion_background_color="2c2a32ff"
completion_existing_color="dfdfdf21"
completion_font_color="aaaaaaff"
completion_scroll_color="ffffffff"
completion_selected_color="434244ff"
control_flow_keyword_color="bf88beff"
current_line_color="ffffff0b"
engine_type_color="4ec9b0ff"
executing_line_color="33cc3366"
function_color="dcdcaaff"
gdscript/function_definition_color="dcdcaaff"
gdscript/node_path_color="6bbcfbff"
keyword_color="569cd6ff"
line_length_guideline_color="333333ff"
line_number_color="aaaaaa66"
mark_color="ff323238"
member_variable_color="9cdcfeff"
number_color="b5cea8ff"
safe_line_number_color="ccf7d3bf"
search_result_border_color="00000000"
search_result_color="613214ff"
selection_color="264f78ff"
string_color="ce9178ff"
symbol_color="d4d4d4ff"
text_color="d4d4d4ff"
text_selected_color="d4d4d4ff"
user_type_color="c6ffedff"
word_highlighted_color="343a40ff"


text:
# Godot syntax themes for Godot 4.x

![Screenshot](https://archive.hugo.pro/.public/godot-syntax-themes.png)

*The Darcula theme in action.*

This repository contains many syntax themes for Godot, for use in the built-in
script editor.

**This branch contains themes *only* compatible with Godot 4.x.**
These themes will not display correctly in Godot 3.x.
[Browse the `3.x` branch](https://github.com/godotengine/godot-syntax-themes/tree/3.x)
to find themes compatible with Godot 3.x.

*Want even more themes? Check out
[base16-godot](https://github.com/Calinou/base16-godot) for automatically
generated themes.*

## Available themes

**See [THEMES.md](/THEMES.md) for preview images.**

| Dark                    | Light                    |
|-------------------------|--------------------------|
| Apprentice              | Quiet Light              |
| Atom Dark               | Solarized Light          |
| Ayu Mirage              | Visual Studio Code Light |
| Darcula                 |                          |
| Dracula                 |                          |
| Gruvbox Dark            |                          |
| Horizon                 |                          |
| Metro                   |                          |
| Monokai                 |                          |
| Nord                    |                          |
| One Dark                |                          |
| One Monokai             |                          |
| Solarized Dark          |                          |
| Visual Studio Code Dark |                          |

## Installation

Place the `.tet` files in your Godot text editor theme directory:

- On Linux: `~/.config/godot/text_editor_themes/`
- On macOS: `~/Library/Application Support/Godot/text_editor_themes/`
- On Windows: `%APPDATA%\Godot\text_editor_themes\`

**Note:** If you installed Godot using Steam, your Godot text editor theme
folder should be placed in `steamapps/common/Godot Engine/editor_data/text_editor_themes/`
in your Steam installation folder.

To change the theme, open a project in the editor, click on **Editor** in the
top menu, then go to the **Editor Settings** then **Text Editor**. You should
now be able to choose the desired theme.

**Tip:** You can clone this Git repository directly into the text editor themes
path (if the destination folder does not exist) using the following command:

```bash
# On Linux:
git clone https://github.com/godotengine/godot-syntax-themes.git ~/.config/godot/text_editor_themes

# On macOS:
git clone https://github.com/godotengine/godot-syntax-themes.git "~/Library/Application Support/Godot/text_editor_themes"

# On Windows:
git clone https://github.com/godotengine/godot-syntax-themes.git "%APPDATA%\Godot\text_editor_themes"
```

## License

Copyright © 2016-present Hugo Locurcio and contributors

Files in this repository are licensed under CC0 1.0 Universal,
see [LICENSE.md](/LICENSE.md) for more information.


text:
status
======

[![Join the chat at DiscordApp](https://img.shields.io/badge/discord-support-blue.svg)](https://discord.gg/vu6Vt9d)

My name is r-lyeh and I code videogames :neckbeard:

This repo is meant to be a semi-conclusive list of projects in which the author is / has participated in.

- The list below is archived. A newer list can be [found here](https://github.com/r-lyeh/statvs).
- The tools below are Public Domain.
- The specifications below are Public Domain (or CC0 licensed), written in Markdown.
- The libraries below are Public Domain (or _ZLIB/LibPNG licensed_), written in C++.
- Most of my source code is (where possible) _cross-platform_, _header-only_ and _self-contained_, and have been used in _commercial games_. It is therefore pretty much battle-tested, but use it at your own risk. No warranties provided.
- Extension convention: `.md` for documents, `.hpp/.cpp` for sources, `.cc` for samples and `.cxx` for tests and benchmarks.

## Released
Fully released / tested; more features may be incoming, but these are considered to be in a 'stable' state.

Tags|Repository
:----------------:|:------------
io|[Apathy (C++03)](http://github.com/r-lyeh/apathy)
 lightweight *path/file/mstream/mmap IO library*.
debug|[Assume (C++03)](http://github.com/r-lyeh/assume)
 smarter *assert replacement* for LHS/RHS values.
tool, image|[Attila (Win32) :star2:](https://github.com/r-lyeh/attila)
 tiny *atlas texture/image packer tool*.
security|[Auth (C++03)](http://github.com/r-lyeh/auth)
 simple, lightweight and safe client-server *authentication system*.
encoding|[Base (C++03)](http://github.com/r-lyeh/base)
 *base91/base85/base64* de/encoders.
serial|[Bourne (C++11)](http://github.com/r-lyeh/bourne)
 lightweight *JSON de/serializer*.
user-interface|[Bubble (C++11)](https://github.com/r-lyeh/bubble)
 simple and lightweight *dialog library for Windows*.
compression|[Bundle (C++03,C++11):star2:](https://github.com/r-lyeh/bundle)
 an embeddable *compression library* that supports 23 algorithms and 2 archivers.
compression,tool|[Bundler (Win32,OSX)](https://github.com/r-lyeh/bundler)
 *command-line archiver* that supports 23 compression algorithms.
data-structures|[Burg (C++11)](https://github.com/r-lyeh/burg)
 simple burg *linear predictor*.
hashing|[Cocoa (C++11):star2:](http://github.com/r-lyeh/cocoa)
 *hashing library* that provides interface for CRC32, CRC64, GCRC, RS, JS, PJW, ELF, BKDR, SBDM, DJB, DJB2, BP, FNV, AP, BJ1, MH2, SHA1, SFH.
encoding|[Collage (C++03)](https://github.com/r-lyeh/collage)
 lightweight library to *diff and patch* arbitrary data.
unit-testing|[Dessert (C++11):star2:](http://github.com/r-lyeh/dessert)
 lightweight *unit-testing framework*.
debug|[Dollar (C++11)](https://github.com/r-lyeh/dollar)
 portable/generic *CPU profiler* with chrome://tracing support.
debug|[DrEcho (C++11)](http://github.com/r-lyeh/DrEcho)
 DrEcho *spices your terminal up*.
debug|[Error64 (C,C++)](http://github.com/r-lyeh/error64)
 Handle custom 64-bit error codes, with extended meta-info
gameplay|[Flare (C++03)](https://github.com/r-lyeh/flare)
 lightweight C++ API to deal with *digital signals/logical buttons*.
network|[Flow (C++11)](https://github.com/r-lyeh/flow)
 lightweight network downloader with native fallbacks aimed to gamedev.
text|[Fmt11 (C++11)](http://github.com/r-lyeh/fmt11)
 tiny *format/mustache templating* library.
gameplay|[Frodo (C++11)](https://github.com/r-lyeh/frodo)
 lightweight *ring dependency framework*.
gameplay|[FSM (C++11)](http://github.com/r-lyeh/fsm)
 lightweight Hierarchical/*Finite-State Machine* (H/FSM) class.
io|[GetOpt (C++11)](http://github.com/r-lyeh/getopt)
 simple *command-line options handler*.
io|[Giant (C++11)](http://github.com/r-lyeh/giant)
 tiny library to *handle little/big endianness*.
debug|[Heal (C++03)](http://github.com/r-lyeh/heal)
 lightweight library to aid and *debug applications*.
render|[Hertz (C++11)](https://github.com/r-lyeh/Hertz)
 simple *framerate locker*.
input|[Hyde (C++03)](http://github.com/r-lyeh/hyde)
 lightweight and simple Human *Inferface Device* (HID) library.
hashing|[ID (C++11)](http://github.com/r-lyeh/id)
 simple *compile-time hasher* and sequential ID generator.
mesh, tool|[Img2Sky (Win32)](http://github.com/r-lyeh/img2sky)
 *vertex-color mesh builder tool* for skyboxes and static geometry.
io|[Journey (C++11)](http://github.com/r-lyeh/journey)
 lightweight *append-only, header-less, journaling file format*.
spec|[JXML](http://github.com/r-lyeh/JXML)
 loss-less representation of *JSON in XML*.
spec|[JXMLex](http://github.com/r-lyeh/JXMLex)
 expressive representation of *JSON in XML*.
network|[Knot (C++03)](http://github.com/r-lyeh/knot)
 lightweight and simple *TCP networking* C++ library.
gameplay|[Kult (C++11):star2:](https://github.com/r-lyeh/kult)
 lightweight *entity/component/system* library.
gameplay|[Live (C++11)](http://github.com/r-lyeh/live)
 *automatic reloader* of variables, featuring type inference.
data-structures|[LRU (C++11)](https://github.com/r-lyeh/lru)
 lightweight *LRU cache structure* for list<T> and map<K,V> containers.
gameplay|[Memo (C++11)](http://github.com/r-lyeh/memo)
 simple and lightweight *factory class*, featuring automatic type casting.
stats|[Metrics (C++11)](https://github.com/r-lyeh/metrics)
 table metrics w/ benchmarking, unit conversions in CSV,TSV,ASCII,markdown.
io|[mINI (C++11)](https://github.com/r-lyeh/mINI)
 very minimal *.INI reader/writer*.
data-structures|[Oak (C++03)](http://github.com/r-lyeh/oak)
 simple and lightweight *tree container*.
design|[Pitch (Markdown)](https://github.com/r-lyeh/pitch)
 forkable game *pitch template*.
spec,tool,image|[Pug](https://github.com/r-lyeh/pug)
 *pug, png with a twist*: lossy image format.
network|[Route66 (C++03)](https://github.com/r-lyeh/route66)
 lightweight *embeddable HTTP server*.
io|[Quant (C++03)](https://github.com/r-lyeh/quant)
 *quantization suite* to/from half-floats, s/unorm bytes, quats and vec3s.
time|[Sand (C++11)](http://github.com/r-lyeh/sand)
 lightweight *timer controller*.
script|[Scriptorium:star:](https://github.com/r-lyeh/scriptorium)
 Game *Scripting Languages benchmarked*.
data|[Sentry (C++11)](https://github.com/r-lyeh/sentry)
 lightweight *data monitor*.
hashing|[Sole (C++11):star2:](http://github.com/r-lyeh/sole)
 lightweight library to generate universally *unique identificators* (UUID) both v1 and v4.
image|[Spot (C++11):star2:](http://github.com/r-lyeh/spot)
 compact and embeddable RGBA/HSLA library that supports *WEBP, JPG, progressive JPG, PNG, TGA, DDS DXT1/2/3/4/5, BMP, PSD, GIF, PVR2/3 (ETC1/PVRTC), KTX (ETC1/PVRTC), PKM (ETC1), HDR, PIC, PNM (PPM/PGM), CRN, PUG, FLIF, CCZ, EXR and vectorial SVG files*.
databases |[SQLight (C++11)](http://github.com/r-lyeh/sqlight)
 lightweight *MySQL client*.
misc |[TinyBits (C,C++)](http://github.com/r-lyeh/tinybits)
 tiny bits and *useful snippets*.
debug|[Tracey (C++11):star2:](http://github.com/r-lyeh/tracey)
 lightweight and simple C++ memory *leak finder* with no dependencies.
data-structures|[Trie (C++11)](http://github.com/r-lyeh/trie)
 lightweight and simple *autocompletion* data structure.
animation |[Tween (C++03)](https://github.com/r-lyeh/tween)
 lightweight *easing library*.
data-structures|[Unordered_map (C++11)](http://github.com/r-lyeh/unordered_map)
 *portable header* for std::unordered_map<K,V> template.
debug|[Unifont (C++11)](http://github.com/r-lyeh/unifont)
 *embeddable console 1bpp font*.
spec, io|[Unify (C++11)](http://github.com/r-lyeh/Unify)
 a function to *normalize resouce identificators*.
network|[VLE (C99, C++03)](https://github.com/r-lyeh/vle)
 simple *variable-length encoder/decoder*.
audio|[Wave (C++11)](http://github.com/r-lyeh/wave)
 lightweight *3D sound wrapper* for OpenAL that supports OGG and MusePack decoding.
utils|[Warp (C++11):star2:](http://github.com/r-lyeh/warp)
 a handy *string interpolator*.
text|[Wire (C++11):star2:](http://github.com/r-lyeh/wire)
 a drop-in *std::string replacement* with extended functionality and safe C/C++ formatters.

## Contributions
Forked projects or in collaboration with other coders.

* [jsonxx](http://github.com/hjiang/jsonxx) -- lightweight *JSON parser* written in C++.
* [IMGUI](http://github.com/r-lyeh/imgui) -- OpenGL 2/3 *Immediate Mode GUI* toolkit.
* [units](http://github.com/r-lyeh/units) -- numerical quantities with units (C++03).
* [unlzma](http://github.com/r-lyeh/unlzma) -- A very compact LZMA decoder (C++03).
* [eval](http://github.com/r-lyeh/eval) -- very simple expression evaluator using shunting-yard algorithm and RPN (C++11).

## Personal
These are mostly released experiments, but are geared towards personal use; as such, they may be of very limited use during development.

* [Bridge](https://github.com/r-lyeh/bridge) -- a standard C++/boost *compatibility layer*, plus a few utils (C++11/C++03).
* [Cash-of-clans](https://github.com/r-lyeh/cash-of-clans) -- a free re-implementation of a working *game economy system*.
* [Crawler](https://github.com/r-lyeh/crawler) -- a quick prototiping platform for Windows (C++11).
* [CLDR](https://github.com/r-lyeh/cldr) -- compact data from the Unicode Common Locale Data Repository.
* [Codex](https://github.com/r-lyeh/codex) -- lightweight and simple C++ library to escape, unescape, read, write and convert from/to different *encoding charsets*.
* [Duty](https://github.com/r-lyeh/duty) -- lightweight *task manager* for parallel coroutines and serial jobs (C++11).
* [Emojis](https://github.com/r-lyeh/emojis) -- emojis, atlased and indexed.
* [RGB332 (tool)](https://github.com/r-lyeh/rgb332) -- custom uniform *RGB332 palette*.
* [Variant](https://github.com/r-lyeh/variant) -- *varying* class that clones javascript behaviour as much as possible (C++11).
* [Malloc-survey](https://github.com/r-lyeh/malloc-survey) -- benchmark for different *memory allocators*.
* [Test-physics](https://github.com/r-lyeh/test-physics) -- benchmark for different *physics integrators*.

## In Progress
Proof-of-concepts until stabilised, currently in mid/active development.

* [AVA](https://github.com/r-lyeh/AVA) -- tiny *3D game engine* in C++, with C and Lua interfaces.
* [Jabba](https://github.com/r-lyeh/jabba) -- (C++11).
* [Play](https://github.com/r-lyeh/play) -- easy *alternative to scripting* in gameplay code.
* [Solace](https://github.com/r-lyeh/solace) -- modern *console replacement*.
* [Wake](https://github.com/r-lyeh/wake) -- a no-brainer *asset build system*. Designed for zombies.
* [Watchmen](https://github.com/r-lyeh/watchmen) -- *asset dependency system* for games in run-time.
* [Juice]() --
* [Graybox]() --
* [Drop]() --
* [Checklist]() --

## Planned
Yet to be committed, removed and/or finished. Someday.

* [Blender](http://github.com/r-lyeh/blender)
* [Gate](https://github.com/r-lyeh/gate)
* [Geo](https://github.com/r-lyeh/geo)
* [Grog](https://github.com/r-lyeh/grog)
* [Ling](https://github.com/r-lyeh/ling)
* [Metatracker](http://github.com/r-lyeh/metatracker)
* [Realm](https://github.com/r-lyeh/realm)
* [Silo](https://github.com/r-lyeh/silo)
* [Skull](https://github.com/r-lyeh/skull)
* [Social](https://github.com/r-lyeh/social)
* [Tint](http://github.com/r-lyeh/tint)
* [Unposix](https://github.com/r-lyeh/unposix)
* [Wrapp](https://github.com/r-lyeh/wrapp)

## On Hold / Abandoned
These are either fully abandoned, or are suffering from an existential crisis.

* [FortFont](http://github.com/r-lyeh/fortfont) -- collection of *western, CJK and iconographic fonts* free for commercial usage.
* [Eve](https://github.com/r-lyeh/eve) -- deprecated game engine where graphics are low priority (C++11).
* [Moon9](https://github.com/r-lyeh/moon9) -- old game engine.
* [Stringbase](http://github.com/r-lyeh/stringbase) -- collaborative effort aimed to *translate common texts* found in videogames and regular apps.
* [Wood](https://github.com/r-lyeh/wood) -- old tree data structure.

## Resume
For completeness.

* [Vitae](http://github.com/r-lyeh/vitae) -- my self-compilable *C++ resume*.

repo_name: r-lyeh-archived/statvs

mime_type: text/html


text:
@echo off
git fetch origin
git reset --hard main
"libs/pythonlib/python.exe" -m pip install --force-reinstall -r requirements.txt

repo_language: Python


text:
# Contributor Covenant Code of Conduct

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
  overall community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or
  advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
  address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series
of actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior,  harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within
the community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.

Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).

[homepage]: https://www.contributor-covenant.org

For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.


text:
@echo off
"libs/pythonlib/python.exe" -m pip install numpy==1.23.0
"libs/pythonlib/python.exe" main.py


text:
import os,sys
import tkinter as tk
import json
import yaml
# Configuration
args = sys.argv[1:]

save_ids = os.path.exists("save_text.txt")

root = tk.Tk()
root.title("RenAI Chat Login")
root.geometry("700x250")
root.configure(background='#333333')

def get_input():
    global GAME_PATH
    global USE_TTS
    global CHAT_MODEL
    global LAUNCH_YOURSELF
    global TTS_MODEL
    global USE_SPEECH_RECOGNITION
    global VOICE_SAMPLE_TORTOISE
    global VOICE_SAMPLE_COQUI
    global CHARACTER_JSON
    USE_TTS = use_tts.get()
    GAME_PATH = game_path.get()
    LAUNCH_YOURSELF = launch_yourself.get()
    CHAT_MODEL = chat_model.get()
    TTS_MODEL = tts_model.get()
    USE_SPEECH_RECOGNITION = use_speech_recognition.get()
    VOICE_SAMPLE_TORTOISE = voice_sample_tortoise.get()
    VOICE_SAMPLE_COQUI = voice_sample_coqui.get()
    CHARACTER_JSON =character_json.get()
    root.destroy()

other_frame = tk.LabelFrame(root,bg='#333333',text="General Settings",fg='white',font=("Helvetica", 16))
other_frame.grid(row=5, column=0)

use_tts = tk.StringVar()
game_path = tk.StringVar()
launch_yourself = tk.StringVar()
chat_model = tk.StringVar()
tts_model = tk.StringVar()
use_speech_recognition = tk.StringVar()
voice_sample_tortoise = tk.StringVar()
voice_sample_coqui = tk.StringVar()
character_json = tk.StringVar()

#General Settings
tk.Label(other_frame, text="Game Path",bg='#333333',fg='white').grid(row=1, column=0)
tk.Label(other_frame, text="Launch Yourself",bg='#333333',fg='white').grid(row=1, column=3)
tk.Label(other_frame, text="Use TTS",bg='#333333',fg='white').grid(row=3, column=0)
tk.Label(other_frame, text="TTS model",bg='#333333',fg='white').grid(row=3, column=3)
tk.Label(other_frame, text="Chatbot Model",bg='#333333',fg='white').grid(row=4, column=0)
tk.Label(other_frame, text="Use Speech Recognition",bg='#333333',fg='white').grid(row=6, column=0)
tk.Label(other_frame, text="Voice Sample (Tortoise)",bg='#333333',fg='white').grid(row=7, column=0)
tk.Label(other_frame, text="Voice Sample (Your TTS)",bg='#333333',fg='white').grid(row=7, column=3)
tk.Label(other_frame, text="Character JSON",bg='#333333',fg='white').grid(row=4, column=2)

#Textual Inputs
tk.Entry(other_frame, textvariable=game_path,width=25).grid(row=1, column=1)

#Scrollable Inputs
all_models = os.listdir("chatbot_models")
all_models = [x for x in all_models if not x.endswith(".txt")]
if len(all_models) == 0:
    all_models = ["No models found"]
chat_menu = tk.OptionMenu(other_frame, chat_model, *all_models)
chat_menu.config(bg='white',fg='black')
chat_menu.grid(row=4, column=1)

tts_menu = tk.OptionMenu(other_frame, tts_model, "Your TTS", "Tortoise TTS")
tts_menu.config( bg='white',fg='black')
tts_menu.grid(row=3, column=4)

all_voices_tortoise = os.listdir("tortoise_audios")
all_voices_tortoise = [x for x in all_voices_tortoise if not x.endswith(".txt")]
voice_menu = tk.OptionMenu(other_frame, voice_sample_tortoise, *all_voices_tortoise)
voice_menu.config( bg='white',fg='black')
voice_menu.grid(row=7, column=1)

all_voices_coquiai = os.listdir("coquiai_audios")
all_voices_coquiai = [x for x in all_voices_coquiai if x.endswith(".wav")]
if len(all_voices_coquiai) == 0:
    all_voices_coquiai = ["No voices found"]
voice_menu = tk.OptionMenu(other_frame, voice_sample_coqui, *all_voices_coquiai)
voice_menu.config( bg='white',fg='black')
voice_menu.grid(row=7, column=4)

all_characters = os.listdir("char_json")
all_characters = [x for x in all_characters if not x.endswith(".txt")]
character_menu = tk.OptionMenu(other_frame, character_json, *all_characters)
character_menu.config( bg='white',fg='black')
character_menu.grid(row=4, column=3)

#Yes/No Inputs
tk.Radiobutton(other_frame, text="Yes", variable=launch_yourself, value=True,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=1, column=4)
tk.Radiobutton(other_frame, text="No", variable=launch_yourself, value=False,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=1, column=5)

tk.Radiobutton(other_frame, text="Yes", variable=use_tts, value=True,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=3, column=1)
tk.Radiobutton(other_frame, text="No", variable=use_tts, value=False,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=3, column=2)

tk.Radiobutton(other_frame, text="Yes", variable=use_speech_recognition, value=True,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=6, column=1)
tk.Radiobutton(other_frame, text="No", variable=use_speech_recognition, value=False,bg='#333333',activeforeground='white',fg='white',activebackground="#333333",selectcolor='#333333').grid(row=6, column=2)

button = tk.Button(root, text="Submit", command=get_input,bg='#FF3399',fg='white')
button.place(relx=0.5, rely=0.95, anchor=tk.CENTER)

#KEEP_CONFIG = int(KEEP_CONFIG)

if not os.path.exists("config.json"):
    #Set default values
    launch_yourself.set(0)
    use_tts.set(0)
    chat_model.set("NO_MODEL_SET")
    tts_model.set("Your TTS")
    use_speech_recognition.set(0)
    voice_sample_tortoise.set("SELECT_A_VOICE")
    voice_sample_coqui.set("talk_13.wav")
    character_json.set("SELECT_A_CHARACTER")
else:
    with open("config.json", "r") as f:
        config = json.load(f)
        GAME_PATH = config["GAME_PATH"]
        USE_TTS = config["USE_TTS"]
        LAUNCH_YOURSELF = config["LAUNCH_YOURSELF"]
        TTS_MODEL = config["TTS_MODEL"]
        CHAT_MODEL = config["CHAT_MODEL"]
        USE_SPEECH_RECOGNITION = config["USE_SPEECH_RECOGNITION"]
        VOICE_SAMPLE_TORTOISE = config["VOICE_SAMPLE_TORTOISE"]
        VOICE_SAMPLE_COQUI = config["VOICE_SAMPLE_COQUI"]
        CHARACTER_JSON = config["CHARACTER_JSON"]
    #Set saved values
    launch_yourself.set(LAUNCH_YOURSELF)
    use_tts.set(USE_TTS)
    chat_model.set(CHAT_MODEL)
    tts_model.set(TTS_MODEL)
    use_speech_recognition.set(USE_SPEECH_RECOGNITION)
    voice_sample_tortoise.set(VOICE_SAMPLE_TORTOISE)
    voice_sample_coqui.set(VOICE_SAMPLE_COQUI)
    character_json.set(CHARACTER_JSON)

root.mainloop()

#Convert string to int (0 or 1, False or True)
USE_TTS = int(USE_TTS)
LAUNCH_YOURSELF = int(LAUNCH_YOURSELF)
USE_SPEECH_RECOGNITION = int(USE_SPEECH_RECOGNITION)

#Save model chosen in chatbot config
with open("chatbot/chatbot_config.yml", "r") as f:
    config_chat = yaml.safe_load(f)
config_chat["model_name"] = "chatbot_models/" + CHAT_MODEL
with open("chatbot/chatbot_config.yml", "w") as f:
    yaml.dump(config_chat, f)

#Save config to a json file
CONFIG = {
    "GAME_PATH": GAME_PATH,
    "USE_TTS": USE_TTS,
    "LAUNCH_YOURSELF": LAUNCH_YOURSELF,
    "TTS_MODEL": TTS_MODEL,
    "CHAT_MODEL": CHAT_MODEL,
    "USE_SPEECH_RECOGNITION": USE_SPEECH_RECOGNITION,
    "VOICE_SAMPLE_TORTOISE": VOICE_SAMPLE_TORTOISE,
    "VOICE_SAMPLE_COQUI": VOICE_SAMPLE_COQUI,
    "CHARACTER_JSON": CHARACTER_JSON
}

with open("config.json", "w") as f:
    json.dump(CONFIG, f)


text:
from chatbot.prompting import build_prompt_for
from chatbot.model import run_raw_inference
import typing as t
import logging

logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)

def inference_fn(model,tokenizer,history: str, user_input: str,
                     generation_settings: t.Dict[str, t.Any],
                     char_settings: t.Dict[str,t.Any],
                     history_length = 8,
                     count = 0,
                     ) -> str:

    char_name = char_settings["char_name"]
    char_persona = char_settings["char_persona"]
    char_greeting = char_settings["char_greeting"]
    world_scenario = char_settings["world_scenario"]
    example_dialogue = char_settings["example_dialogue"]

    #print(char_persona)
    if count == 0 and char_greeting is not None:
        return f"{char_greeting}"

    prompt = build_prompt_for(history=history,
                                user_message=user_input,
                                char_name=char_name,
                                char_persona=char_persona,
                                example_dialogue=example_dialogue,
                                world_scenario=world_scenario,
                                history_lenght=history_length)

    model_output = run_raw_inference(model, tokenizer, prompt,
                                            user_input, **generation_settings)

    #remove last line and keep the last line before
    last_line = model_output.splitlines()[-1]
    list_lines = model_output.splitlines()
    if last_line.startswith("You:"):
        bot_message = list_lines[-2]
    else:
        bot_message = last_line

    #remove the char name at the beginning of the line
    bot_message = bot_message.replace(f"{char_name}: ","")

    return bot_message


text:
from pathlib import Path

from TTS.utils.manage import ModelManager
from TTS.utils.synthesizer import Synthesizer

class TTS:
    """TODO: Add voice conversion and Capacitron support."""

    def __init__(self, model_name: str = None, progress_bar: bool = True, gpu=False):
        """🐸TTS python interface that allows to load and use the released models.
        Example with a multi-speaker model:
            >>> from TTS.api import TTS
            >>> tts = TTS(TTS.list_models()[0])
            >>> wav = tts.tts("This is a test! This is also a test!!", speaker=tts.speakers[0], language=tts.languages[0])
            >>> tts.tts_to_file(text="Hello world!", speaker=tts.speakers[0], language=tts.languages[0], file_path="output.wav")
        Example with a single-speaker model:
            >>> tts = TTS(model_name="tts_models/de/thorsten/tacotron2-DDC", progress_bar=False, gpu=False)
            >>> tts.tts_to_file(text="Ich bin eine Testnachricht.", file_path="output.wav")
        Args:
            model_name (str, optional): Model name to load. You can list models by ```tts.models```. Defaults to None.
            progress_bar (bool, optional): Whether to pring a progress bar while downloading a model. Defaults to True.
            gpu (bool, optional): Enable/disable GPU. Some models might be too slow on CPU. Defaults to False.
        """
        self.manager = ModelManager(models_file=self.get_models_file_path(), progress_bar=progress_bar, verbose=False)
        self.synthesizer = None
        if model_name:
            self.load_model_by_name(model_name, gpu)

    @property
    def models(self):
        return self.manager.list_tts_models()

    @property
    def is_multi_speaker(self):
        if hasattr(self.synthesizer.tts_model, "speaker_manager") and self.synthesizer.tts_model.speaker_manager:
            return self.synthesizer.tts_model.speaker_manager.num_speakers > 1
        return False

    @property
    def is_multi_lingual(self):
        if hasattr(self.synthesizer.tts_model, "language_manager") and self.synthesizer.tts_model.language_manager:
            return self.synthesizer.tts_model.language_manager.num_languages > 1
        return False

    @property
    def speakers(self):
        if not self.is_multi_speaker:
            return None
        return self.synthesizer.tts_model.speaker_manager.speaker_names

    @property
    def languages(self):
        if not self.is_multi_lingual:
            return None
        return self.synthesizer.tts_model.language_manager.language_names

    @staticmethod
    def get_models_file_path():
        return Path(__file__).parent / ".models.json"

    @staticmethod
    def list_models():
        manager = ModelManager(models_file=TTS.get_models_file_path(), progress_bar=False, verbose=False)
        return manager.list_tts_models()

    def download_model_by_name(self, model_name: str):
        model_path, config_path, model_item = self.manager.download_model(model_name)
        if model_item["default_vocoder"] is None:
            return model_path, config_path, None, None
        vocoder_path, vocoder_config_path, _ = self.manager.download_model(model_item["default_vocoder"])
        return model_path, config_path, vocoder_path, vocoder_config_path

    def load_model_by_name(self, model_name: str, gpu: bool = False):
        model_path, config_path, vocoder_path, vocoder_config_path = self.download_model_by_name(model_name)
        # init synthesizer
        # None values are fetch from the model
        self.synthesizer = Synthesizer(
            tts_checkpoint=model_path,
            tts_config_path=config_path,
            tts_speakers_file=None,
            tts_languages_file=None,
            vocoder_checkpoint=vocoder_path,
            vocoder_config=vocoder_config_path,
            encoder_checkpoint=None,
            encoder_config=None,
            use_cuda=gpu,
        )

    def tts(self, text: str, speaker: str = None, language: str = None):
        """Convert text to speech.
        Args:
            text (str):
                Input text to synthesize.
            speaker (str, optional):
                Speaker name for multi-speaker. You can check whether loaded model is multi-speaker by
                `tts.is_multi_speaker` and list speakers by `tts.speakers`. Defaults to None.
            language (str, optional):
                Language code for multi-lingual models. You can check whether loaded model is multi-lingual
                `tts.is_multi_lingual` and list available languages by `tts.languages`. Defaults to None.
        """

        wav = self.synthesizer.tts(
            text=text,
            speaker_name=speaker,
            language_name=language,
            speaker_wav=None,
            reference_wav=None,
            style_wav=None,
            style_text=None,
            reference_speaker_name=None,
        )
        return wav

    def tts_to_file(self, text: str, speaker: str = None, language: str = None, file_path: str = "output.wav"):
        """Convert text to speech.
        Args:
            text (str):
                Input text to synthesize.
            speaker (str, optional):
                Speaker name for multi-speaker. You can check whether loaded model is multi-speaker by
                `tts.is_multi_speaker` and list speakers by `tts.speakers`. Defaults to None.
            language (str, optional):
                Language code for multi-lingual models. You can check whether loaded model is multi-lingual
                `tts.is_multi_lingual` and list available languages by `tts.languages`. Defaults to None.
            file_path (str, optional):
                Output file path. Defaults to "output.wav".
        """
        wav = self.tts(text=text, speaker=speaker, language=language)
        self.synthesizer.save_wav(wav=wav, path=file_path)

class my_TTS(TTS):
    def __init__(self, *args, **kwargs):
        super(my_TTS, self).__init__(*args, **kwargs)

    def tts(self, text: str, speaker: str = None, language: str = None,speaker_wav: str = None, reference_wav: str = None, style_wav: str = None, style_text: str = None, reference_speaker_name: str = None):
        """Synthesize text to speech."""

        wav = self.synthesizer.tts(
            text=text,
            speaker_name=speaker,
            language_name=language,
            speaker_wav=speaker_wav,
            reference_wav=reference_wav,
            style_wav=style_wav,
            style_text=style_text,
            reference_speaker_name=reference_speaker_name,
        )
        return wav

    def tts_to_file(self, text: str, speaker: str = None, language: str = None, file_path: str = "output.wav", speaker_wav: str = None, reference_wav: str = None, style_wav: str = None, style_text: str = None, reference_speaker_name: str = None):
        wav = self.tts(text=text, speaker=speaker, language=language,speaker_wav=speaker_wav, reference_wav=reference_wav, style_wav=style_wav, style_text=style_text, reference_speaker_name=reference_speaker_name)
        self.synthesizer.save_wav(wav=wav, path=file_path)


text:
import torch
from pathlib import Path

def infer(spec_gen_model, vocoder_model, str_input, speaker=None):
    """
    Synthesizes spectrogram and audio from a text string given a spectrogram synthesis and vocoder model.

    Args:
        spec_gen_model: Spectrogram generator model (FastPitch in our case)
        vocoder_model: Vocoder model (HiFiGAN in our case)
        str_input: Text input for the synthesis
        speaker: Speaker ID

    Returns:
        spectrogram and waveform of the synthesized audio.
    """
    with torch.no_grad():
        parsed = spec_gen_model.parse(str_input)
        if speaker is not None:
            speaker = torch.tensor([speaker]).long().to(device=spec_gen_model.device)
        spectrogram = spec_gen_model.generate_spectrogram(tokens=parsed, speaker=speaker)
        audio = vocoder_model.convert_spectrogram_to_audio(spec=spectrogram)

    if spectrogram is not None:
        if isinstance(spectrogram, torch.Tensor):
            spectrogram = spectrogram.to('cpu').numpy()
        if len(spectrogram.shape) == 3:
            spectrogram = spectrogram[0]
    if isinstance(audio, torch.Tensor):
        audio = audio.to('cpu').numpy()
    return spectrogram, audio

def get_best_ckpt_from_last_run(
        base_dir,
        new_speaker_id,
        duration_mins,
        mixing_enabled,
        original_speaker_id,
        model_name="FastPitch"
    ):
    mixing = "no_mixing" if not mixing_enabled else "mixing"

    d = f"{original_speaker_id}_to_{new_speaker_id}_{mixing}_{duration_mins}_mins"

    exp_dirs = list([i for i in (Path(base_dir) / d / model_name).iterdir() if i.is_dir()])
    last_exp_dir = sorted(exp_dirs)[-1]

    last_checkpoint_dir = last_exp_dir / "checkpoints"

    last_ckpt = list(last_checkpoint_dir.glob('*-last.ckpt'))

    if len(last_ckpt) == 0:
        raise ValueError(f"There is no last checkpoint in {last_checkpoint_dir}.")

    return str(last_ckpt[0])


text:
from socket import AF_INET, socket, SOCK_STREAM
from threading import Thread
import os,sys
import subprocess
import time
import simpleaudio as sa

try:
    from tts_api import my_TTS
except:
    my_TTS = None

import torch
import numpy as np

import IPython.display as ipd
from login_screen import CONFIG
import re

import yaml
import json

GAME_PATH = CONFIG["GAME_PATH"]
USE_TTS = CONFIG["USE_TTS"]
LAUNCH_YOURSELF = CONFIG["LAUNCH_YOURSELF"]
TTS_MODEL = CONFIG["TTS_MODEL"]
USE_SPEECH_RECOGNITION = CONFIG["USE_SPEECH_RECOGNITION"]
VOICE_SAMPLE_TORTOISE = CONFIG["VOICE_SAMPLE_TORTOISE"]
VOICE_SAMPLE_COQUI = CONFIG["VOICE_SAMPLE_COQUI"]
CHARACTER_JSON = CONFIG["CHARACTER_JSON"]

#Disable print from TTS Coqui AI
class HiddenPrints:
    def __enter__(self):
        self._original_stdout = sys.stdout
        sys.stdout = open(os.devnull, 'w')

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout.close()
        sys.stdout = self._original_stdout

######LOAD CHATBOT CONFIG######
from chatbot.model import build_model_and_tokenizer_for
from run_chatbot import inference_fn
import gc

with open("chatbot/chatbot_config.yml", "r") as f:
    CHAT_CONFIG = yaml.safe_load(f)

with open(f"char_json/{CHARACTER_JSON}", "r") as f:
    char_settings = json.load(f)
f.close()

model_name = CHAT_CONFIG["model_name"]
gc.collect()
torch.cuda.empty_cache()
chat_model, tokenizer = build_model_and_tokenizer_for(model_name)

generation_settings = {
    "max_new_tokens": CHAT_CONFIG["max_new_tokens"],
    "temperature": CHAT_CONFIG["temperature"],
    "repetition_penalty": CHAT_CONFIG["repetition_penalty"],
    "top_p": CHAT_CONFIG["top_p"],
    "top_k": CHAT_CONFIG["top_k"],
    "do_sample": CHAT_CONFIG["do_sample"],
    "typical_p":CHAT_CONFIG["typical_p"],
}

context_size = CHAT_CONFIG["context_size"]

with open("chat_history.txt", "a") as chat_history:
    chat_history.write("Conversation started at: " + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + "\n")
#################################

#########Load the TTS model##########
with HiddenPrints():
    if USE_TTS:
        from tortoise.api import TextToSpeech,MODELS_DIR
        from tortoise.utils.audio import load_voices
        from voicefixer import VoiceFixer
        if TTS_MODEL == "Your TTS":
            tts_model = my_TTS(model_name="tts_models/multilingual/multi-dataset/your_tts")
            sampling_rate = 16000
        elif TTS_MODEL == "Tortoise TTS":
            tts_model = TextToSpeech(
                    models_dir=MODELS_DIR,
                    high_vram=False,
                    kv_cache=True,
                )
            voice_samples, conditioning_latents = load_voices([VOICE_SAMPLE_TORTOISE], ["tortoise_audios"])
            vfixer = VoiceFixer()
            sampling_rate = 24000
        else:
            print("No TTS model selected")

def play_TTS(step,msg,play_obj):
    if USE_TTS:
        print("Using TTS")
        if step > 0:
            play_obj.stop()
        msg_audio = msg.replace("\n"," ")
        msg_audio = msg_audio.replace("{i}","")
        msg_audio = msg_audio.replace("{/i}",".")
        msg_audio = msg_audio.replace("~","!")
        msg_audio = emoji_pattern.sub(r'', msg_audio)
        msg_audio = uni_chr_re.sub(r'', msg_audio)
        with HiddenPrints():
            if TTS_MODEL == "Your TTS":
                audio = tts_model.tts(text=msg_audio,speaker_wav=f'coquiai_audios/{VOICE_SAMPLE_COQUI}', language='en')
            elif TTS_MODEL == "Tortoise TTS":
                gen, _ = tts_model.tts(
                        text=msg_audio,
                        k=1,
                        voice_samples=voice_samples,
                        conditioning_latents=conditioning_latents,
                        num_autoregressive_samples=8,
                        diffusion_iterations=20,
                        sampler="ddim",
                        return_deterministic_state=True,
                        latent_averaging_mode=1,
                        length_penalty=1.8,
                        max_mel_tokens=500,
                        cond_free_k=2,
                        top_p=0.85,
                        repetition_penalty=2.,
                    )
                audio = gen.squeeze(0).cpu().numpy()

        audio = ipd.Audio(audio, rate=sampling_rate)
        play_obj = sa.play_buffer(audio.data, 1, 2, sampling_rate)
        return play_obj
#####################################

####Load the speech recognizer#####
if USE_SPEECH_RECOGNITION:
    import speech_recognition as sr
    import whisper

    english = True
    def init_stt(model="base", english=True,energy=300, pause=0.8, dynamic_energy=False):
        if model != "large" and english:
            model = model + ".en"
        audio_model = whisper.load_model(model)
        r = sr.Recognizer()
        r.energy_threshold = energy
        r.pause_threshold = pause
        r.dynamic_energy_threshold = dynamic_energy
        return r,audio_model

    r,audio_model = init_stt()
#####################################

GAME_PATH = GAME_PATH.replace("\\", "/")
clients = {}
addresses = {}
HOST = '127.0.0.1'
PORT = 12346
BUFSIZE = 1024
ADDRESS = (HOST, PORT)
SERVER = socket(AF_INET, SOCK_STREAM)
SERVER.bind(ADDRESS)
queued = False
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

emoji_pattern = re.compile("["
    u"\U0001F600-\U0001F64F"  # emoticons
    u"\U0001F300-\U0001F5FF"  # symbols & pictographs
    u"\U0001F680-\U0001F6FF"  # transport & map symbols
    u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
    u"\U00002500-\U00002BEF"  # chinese char
    u"\U00002702-\U000027B0"
    u"\U00002702-\U000027B0"
    u"\U000024C2-\U0001F251"
    u"\U0001f926-\U0001f937"
    u"\U00010000-\U0010ffff"
    u"\u2640-\u2642"
    u"\u2600-\u2B55"
    u"\u200d"
    u"\u23cf"
    u"\u23e9"
    u"\u231a"
    u"\ufe0f"  # dingbats
    u"\u3030"
    u"\u2014"
    "]+", flags=re.UNICODE)

uni_chr_re = re.compile(r'\\u[0-9a-fA-F]{4}')

#Launch the game
if not LAUNCH_YOURSELF:
    subprocess.Popen(GAME_PATH+'/RenAIChat.exe')

def listen():
	""" Wait for incoming connections """
	print("Waiting for connection...")
	while True:
		client, client_address = SERVER.accept()
		print("%s:%s has connected." % client_address)
		addresses[client] = client_address
		Thread(target = call, args = (client,)).start()

def call(client):
    thread = Thread(target=listenToClient, args=(client,), daemon=True)
    thread.start()

def sendMessage(msg, name=""):
    """ send message to all users present in
    the chat room"""
    for client in clients:
        client.send(bytes(name, "utf8") + msg)

def send_answer(received_msg,msg):
    action_to_take = "none"
    action_to_take = action_to_take.encode("utf-8")
    emotion = "".encode("utf-8")
    msg = msg.encode("utf-8")
    msg_to_send = msg + b"/g" + emotion + b"/g" + action_to_take
    sendMessage(msg_to_send)

def listenToClient(client):
    """ Get client username """
    name = "User"
    clients[client] = name
    launched = False
    chat_count = 0
    play_obj = None
    if os.path.exists("char_history.txt"):
        history = open("char_history.txt","r").read()
        #Remove lines with the pattern "Conversation started at: 2023-02-14 14:14:17"
        history = re.sub(r"Conversation started at: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}","",history)
    else:
        history = ""
    while True:
        received_msg = client.recv(BUFSIZE).decode("utf-8") #Message indicating the mode used (chatbot,camera_int or camera)
        received_msg = received_msg.split("/m")
        rest_msg = received_msg[1]
        received_msg = received_msg[0]
        if received_msg == "chatbot":
            if '/g' in rest_msg:
                received_msg , step = rest_msg.split("/g")
            else:
                received_msg = client.recv(BUFSIZE).decode("utf-8") #Message containing the user input
                received_msg , step = received_msg.split("/g")
            step = int(step)
            #Speech to text
            if received_msg == "begin_record":
                if USE_SPEECH_RECOGNITION:
                    with sr.Microphone(sample_rate=16000) as source:
                        sendMessage("yes".encode("utf-8"))
                        #get and save audio to wav file
                        audio = r.listen(source)
                        torch_audio = torch.from_numpy(np.frombuffer(audio.get_raw_data(), np.int16).flatten().astype(np.float32) / 32768.0)
                        audio_data = torch_audio
                        if english:
                            result = audio_model.transcribe(audio_data,language='english')
                        else:
                            result = audio_model.transcribe(audio_data)
                        received_msg = result['text']
                else:
                    sendMessage("no".encode("utf-8"))
                    continue

            print("User: "+received_msg)

            while True:
                if chat_count == 0:
                    sendMessage("server_ok".encode("utf-8"))
                    ok_ready = client.recv(BUFSIZE).decode("utf-8")
                    bot_message = inference_fn(chat_model,tokenizer,history, "",generation_settings,char_settings,history_length=context_size,count=chat_count)
                else:
                    bot_message = inference_fn(chat_model,tokenizer,history, received_msg,generation_settings,char_settings,history_length=context_size,count=chat_count)
                    history = history + "\n" + f"You: {received_msg}" + "\n" + f"{bot_message}"
                if received_msg != "QUIT":
                    if received_msg == "REGEN":
                        history.replace("\n" + f"You: {received_msg}" + "\n" + f"{bot_message}","")
                        bot_message = inference_fn(chat_model,tokenizer,history, received_msg,generation_settings,char_settings,history_length=context_size,count=chat_count)
                    bot_message = bot_message.replace("<USER>","Player")
                    play_obj = play_TTS(step,bot_message,play_obj)
                    print("Sent: "+ bot_message)
                    send_answer(received_msg,bot_message)
                    chat_count += 1
                    if chat_count > 1:
                        with open("chat_history.txt", "a",encoding="utf-8") as f:
                            f.write(f"You: {received_msg}" + "\n" + f'{char_settings["char_name"]}: {bot_message}' + "\n")
                break

if __name__ == "__main__":
    SERVER.listen(5)
    ACCEPT_THREAD = Thread(target=listen)
    ACCEPT_THREAD.start()
    ACCEPT_THREAD.join()
    SERVER.close()


text:
playwright~=1.31.0
simpleaudio~=1.0.4
IPython~=8.10.0
tensorflow-macos~=2.11.0
opencv-python~=4.7.0.72
transformers~=4.26.1
Pillow~=9.4.0
SpeechRecognition~=3.9.0
git+https://github.com/openai/whisper.git
tk~=0.1.0
bitsandbytes~=0.37.0
numpy==1.23.0


text:
playwright~=1.31.0
#simpleaudio-1.0.4-cp38-cp38-win_amd64.whl
IPython~=8.10.0
tensorflow~=2.11.0
opencv-python~=4.7.0.72
transformers~=4.26.1
Pillow~=9.4.0
SpeechRecognition~=3.9.0
git+https://github.com/openai/whisper.git
tk~=0.1.0
bitsandbytes~=0.37.0
numpy==1.23.0
PyAudio


text:
 :email: RenAI Chat

This project aims to create a fun interface to use conversational models in the form of a Visual Novel in Renpy.
It's using multiple AI models:
- [Pygmalion](https://huggingface.co/PygmalionAI) conversational AI or other Huggingface models
- [TTS Coqui-AI](https://github.com/coqui-ai/TTS) and [Tortoise-TTS](https://github.com/152334H/tortoise-tts-fast) for Text to Speech
- [OpenAI Whisper](https://github.com/openai/whisper) with [microphone option](https://github.com/mallorbc/whisper_mic) for Speech to Text

# Overview

## :boom: Installation

Check out the wiki [page](https://github.com/Rubiksman78/RenAI-Chat/wiki).

## :fire: Features

- Speak without scripted text using the latest chatbots
- Clone your character voice with a Text to Speech module using extracts of voiced dialogues
- Talk with your own voice thanks to Speech recognition

## :eyeglasses: How to add chatbot models

- Check the [wiki](https://github.com/Rubiksman78/RenAI-Chat/wiki/Chatbots-info) to setup your GPU and for pratical info about chatbots to use
- On Huggingface website go to the model page, for example `https://huggingface.co/PygmalionAI/pygmalion-2.7b`

- Follow the instructions to download the model like this on your Command Prompt or Powershell opened wherever you want:
```
git lfs install
git clone https://huggingface.co/PygmalionAI/pygmalion-2.7b
```
- Once the download is finished, put the folder created in `chatbot_models` in the RenAIChat folder. You will be able to choose it in your next login !

## :microphone: Customize voice

For Your TTS model (worse but faster, ~5s per turn):
You can change the voice used by placing extracts in the `coquiai_audios` folder. The longer the extract, the longer the TTS will take to generate the audio at each turn (~1min is good). It has to be `.wav` audio files, use an online converter if you have `.mp3` files.

For Tortoise TTS model (better but slower, ~40s per turn): You can change the voice samples in `tortoise_voices_` folder. Create your own character by adding a folder with the name of your character and put the audio samples in it. The samples must be around 10 seconds long and at the number of 5 maximum. Prefer `.wav` files too.

On CPU, it can take 10x more time to generate the voice (Tortoise TTS can have unexpected behaviour on CPU)

## :paperclip: Customize character json

Put the json of your character in the folder `char_json` in the format:
```
{
"char_name":...,
"char_persona":...,
"char_greeting":...,
"world_scenario":...,
"example_dialogue":"<START>\nYou:... \nCharName:...\n..."
}
```

## :camera: Customize game appearance

In your RenAIChat folder with the Renpy files (so the other zip file `RenAIChat-version-pc/mac`), add a transparent sprite of your character with the filename `char.png` and add a background named `bg.png` in the folder `game/images`. Don't forget to resize your BG to 1920x1080 and your character to the size you want (without being more than 1920x1080).

# Python installation

## ❓Installation

- Clone the repository or download the latest release (`source code.zip`)
- Go to the project folder with your favorite IDE
- Be sure to have Python installed (3.8 or 3.9), it is not tested and functional before 3.7 and after 3.10.

To setup all the libraries:
- Run these commands in a terminal opened within the project folder to install the packages:
    ```
    pip install -r requirements.txt
    ```
- If you have a CUDA compatible GPU, run this too:
    ```
    pip install --force torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu117
    ```
- If there was an error returned during the installation of the packages, delete the corresponding line in `requirements.txt` and dowload the package concerned manually
- To download TTS (with Coqui AI TTS), run these commands:
    ```
    git clone https://github.com/coqui-ai/TTS
    cd TTS
    pip install -e .
    cd ../
    ```
- To download TTS (with Tortoise-TTS), run these commands:
    ```
    git clone https://github.com/152334H/tortoise-tts-fast
    cd tortoise-tts-fast
    pip install -e .
    cd ../
    ```
- `simpleaudio` or other packages might need to install Visual Studio C++ Tools too (see tutorial [here](https://stackoverflow.com/questions/64261546/how-to-solve-error-microsoft-visual-c-14-0-or-greater-is-required-when-inst)), for `simpleaudio` follow [this](https://stackoverflow.com/questions/67312738/error-command-errored-out-with-exit-status-1-python-when-installing-simple)
- If you want to use Huggingface models with int8, follow these intructions:
  - Download these 2 dll files from [here](https://github.com/DeXtmL/bitsandbytes-win-prebuilt). Move those files in your python packages folder, on Windows it is something like `C:\Users\MyName\AppData\Local\Programs\Python\Python39\Lib\site-packages\bitsandbytes`
  - Edit `bitsandbytes\cuda_setup\main.py`:
    - Change `ct.cdll.LoadLibrary(binary_path)` to `ct.cdll.LoadLibrary(str(binary_path))` two times in the file.
    - Replace the this line ```if not torch.cuda.is_available(): return 'libsbitsandbytes_cpu.so', None, None, None, None``` with ```if torch.cuda.is_available(): return 'libbitsandbytes_cuda116.dll', None, None, None, None```
- Run the script with `python main.py`
- For troubleshooting and other issues, don't hesitate to submit an issue


text:
import logging
import typing as t

from chatbot.parsing import parse_messages_from_str

logger = logging.getLogger(__name__)

def build_prompt_for(
    history: str,
    user_message: str,
    char_name: str,
    char_persona: t.Optional[str] = None,
    example_dialogue: t.Optional[str] = None,
    world_scenario: t.Optional[str] = None,
    history_lenght: int = 8,
) -> str:
    '''Converts all the given stuff into a proper input prompt for the model.'''

    # If example dialogue is given, parse the history out from it and append
    # that at the beginning of the dialogue history.
    example_history = parse_messages_from_str(
        example_dialogue, ["You", char_name]) if example_dialogue else []
    if history:
        history = parse_messages_from_str(history, ["You", char_name] if history else [])
    else:
        history = []
    concatenated_history = [*example_history, *history]

    # Construct the base turns with the info we already have.
    prompt_turns = [
        "<START>",
        *concatenated_history[-history_lenght:],
        f"You: {user_message}",
        f"{char_name}:",
    ]

    # If we have a scenario or the character has a persona definition, add those
    # to the beginning of the prompt.
    if world_scenario:
        prompt_turns.insert(
            0,
            f"Scenario: {world_scenario}",
        )

    if char_persona:
        prompt_turns.insert(
            0,
            f"{char_name}'s Persona: {char_persona}",
        )

    # Done!
    logger.debug("Constructed prompt is: `%s`", prompt_turns)
    prompt_str = "\n".join(prompt_turns)
    return prompt_str


text:
char_json: null
context_size: 8
do_sample: true
max_new_tokens: 100
model_name: chatbot_models/No models found
repetition_penalty: 1.2
temperature: 0.65
top_k: 0
top_p: 0.9
typical_p: 1.0
use_int_8: true


text:
import logging
import typing as t

import torch
import transformers

import yaml

logger = logging.getLogger(__name__)

with open("chatbot/chatbot_config.yml", "r") as f:
        CHAT_CONFIG = yaml.safe_load(f)

USE_INT_8 = CHAT_CONFIG["use_int_8"]

def build_model_and_tokenizer_for(
    model_name: str
) -> t.Tuple[transformers.AutoModelForCausalLM, transformers.AutoTokenizer]:
    '''Sets up the model and accompanying objects.'''

    logger.info(f"Loading tokenizer for {model_name}")
    tokenizer = transformers.AutoTokenizer.from_pretrained(model_name)

    bad_words_ids = [
        tokenizer(bad_word, add_special_tokens=False).input_ids
        for bad_word in _build_bad_words_list_for(model_name)
    ]
    logger.info(f"Loading the {model_name} model")
    model = transformers.AutoModelForCausalLM.from_pretrained(
        model_name,
        bad_words_ids=bad_words_ids,
        device_map="auto",
        load_in_8bit=USE_INT_8,
        torch_dtype=torch.float16,
        offload_folder="offload",
        # offload_state_dict=True,
        max_memory={0:"5890MiB","cpu":"99GiB"},
    )
    logger.info("Model and tokenizer are ready")
    return model, tokenizer

def run_raw_inference(model: transformers.AutoModelForCausalLM,
                      tokenizer: transformers.AutoTokenizer, prompt: str,
                      user_message: str, **kwargs: t.Any) -> str:
    '''
    Runs inference on the model, and attempts to returns only the newly
    generated text.

    :param model: Model to perform inference with.
    :param tokenizer: Tokenizer to tokenize input with.
    :param prompt: Input to feed to the model.
    :param user_message: The user's raw message, exactly as appended to the end
        of `prompt`. Used for trimming the original input from the model output.
    :return: Decoded model generation.
    '''
    tokenized_items = tokenizer(prompt, return_tensors="pt",truncation=True,max_length=1024,add_special_tokens=True).to("cuda")

    stopping_criteria_list = transformers.StoppingCriteriaList([
        _SentinelTokenStoppingCriteria(
            sentinel_token_ids=tokenizer(
                "\nYou:",
                add_special_tokens=False,
                return_tensors="pt",
            ).input_ids.to("cuda"),
            starting_idx=tokenized_items.input_ids.shape[-1])
    ])

    with torch.no_grad():
        logits = model.generate(stopping_criteria=stopping_criteria_list,
                                pad_token_id=tokenizer.eos_token_id,
                                **tokenized_items,
                                **kwargs)
    output = tokenizer.decode(logits[0], skip_special_tokens=True)

    logger.debug("Before trimming, model output was: `%s`", output)
    return output

def _build_bad_words_list_for(_model_name: str) -> t.List[str]:
    '''Builds a list of bad words for the given model.'''

    # NOTE(11b): This was implemented as a function because each model size
    # seems to have it quirks at the moment, but this is a rushed implementation
    # so I'm not handling that, hence the dumb return here.
    return ["Persona:", "Scenario:", "<START>"]

class _SentinelTokenStoppingCriteria(transformers.StoppingCriteria):

    def __init__(self, sentinel_token_ids: torch.LongTensor,
                 starting_idx: int):
        transformers.StoppingCriteria.__init__(self)
        self.sentinel_token_ids = sentinel_token_ids
        self.starting_idx = starting_idx

    def __call__(self, input_ids: torch.LongTensor,
                 _scores: torch.FloatTensor) -> bool:
        for sample in input_ids:
            trimmed_sample = sample[self.starting_idx:]
            # Can't unfold, output is still too tiny. Skip.
            if trimmed_sample.shape[-1] < self.sentinel_token_ids.shape[-1]:
                continue

            for window in trimmed_sample.unfold(
                    0, self.sentinel_token_ids.shape[-1], 1):
                if torch.all(torch.eq(self.sentinel_token_ids, window)):
                    return True
        return False


text:
import re
import typing as t

BAD_CHARS_FOR_REGEX_REGEX = re.compile(r"[-\/\\^$*+?.()|[\]{}]")

def _sanitize_string_for_use_in_a_regex(string: str) -> str:
    '''Sanitizes `string` so it can be used inside of a regexp.'''
    return BAD_CHARS_FOR_REGEX_REGEX.sub(r"\\\g<0>", string)

def parse_messages_from_str(string: str, names: t.List[str]) -> t.List[str]:
    '''
    Given a big string containing raw chat history, this function attempts to
    parse it out into a list where each item is an individual message.
    '''
    sanitized_names = [
        _sanitize_string_for_use_in_a_regex(name) for name in names
    ]

    speaker_regex = re.compile(rf"^({'|'.join(sanitized_names)}): ?",
                               re.MULTILINE)

    message_start_indexes = []
    for match in speaker_regex.finditer(string):
        message_start_indexes.append(match.start())

    prev_start_idx = message_start_indexes[0]
    messages = []

    for start_idx in message_start_indexes[1:]:
        message = string[prev_start_idx:start_idx].strip()
        messages.append(message)
        prev_start_idx = start_idx
    #append the last message
    message = string[prev_start_idx:].strip()
    messages.append(message)
    return messages

def serialize_chat_history(history: t.List[str]) -> str:
    '''Given a structured chat history object, collapses it down to a string.'''
    return "\n".join(history)


text:
# coding:utf-8

import os
import csv
import json
import time
import pickle
import warnings
import numpy as np
import pandas as pd

import torch
import torch.nn as nn
from transformers import BertTokenizer

from modeling.modeling_nezha.modeling import NeZhaPreTrainedModel, NeZhaModel

class NeZhaSequenceClassification(NeZhaPreTrainedModel):
    def __init__(self, config):
        super().__init__(config)
        self.num_labels = 3
        self.bert = NeZhaModel(config)
        self.classifier = nn.Linear(config.hidden_size, self.num_labels)
        self.init_weights()

    def forward(
            self,
            input_ids=None,
            attention_mask=None,
            token_type_ids=None,
            position_ids=None,
            head_mask=None,
            inputs_embeds=None,
            labels=None
    ):

        outputs = self.bert(
            input_ids,
            attention_mask=attention_mask,
            token_type_ids=token_type_ids,
            head_mask=head_mask,
            inputs_embeds=inputs_embeds,
        )

        pooled_output = outputs[1]
        logits = self.classifier(pooled_output)

        outputs = (logits,) + outputs[2:]

        if labels is not None:
            # loss_fct = LabelSmoothingLoss(smoothing=0.01)
            loss_fct = nn.CrossEntropyLoss()
            loss = loss_fct(logits.view(-1, self.num_labels), labels.view(-1))
            outputs = (loss,) + outputs

        return outputs

def argmax(res):
    k, tmp = 0, 0
    for i in range(len(res)):
        if res[i] > tmp:
            tmp = res[i]
            k = i

    return k

def batch_loader(config, src, seg, mask):
    ins_num = src.size()[0]
    batch_size = config['batch_size']
    for i in range(ins_num // batch_size):
        src_batch = src[i * batch_size: (i + 1) * batch_size, :]
        seg_batch = seg[i * batch_size: (i + 1) * batch_size, :]
        mask_batch = mask[i * batch_size: (i + 1) * batch_size, :]
        yield src_batch, seg_batch, mask_batch
    if ins_num > ins_num // batch_size * batch_size:
        src_batch = src[ins_num // batch_size * batch_size:, :]
        seg_batch = seg[ins_num // batch_size * batch_size:, :]
        mask_batch = mask[ins_num // batch_size * batch_size:, :]
        yield src_batch, seg_batch, mask_batch

def read_dataset(config):
    start = time.time()
    tokenizer = BertTokenizer.from_pretrained(config['init_model_path'])
    dataset, r_dataset = [], []
    seq_length = config['max_seq_len']

    with open(config['test_path'], 'r', encoding='utf-8') as f:
        for line_id, line in enumerate(f):
            sent_a, sent_b = line.strip().split('\t')
            src_a = tokenizer.convert_tokens_to_ids(["[CLS]"] + tokenizer.tokenize(sent_a) + ["[SEP]"])
            src_b = tokenizer.convert_tokens_to_ids(tokenizer.tokenize(sent_b) + ["[SEP]"])
            src = src_a + src_b
            seg = [0] * len(src_a) + [1] * len(src_b)
            mask = [1] * len(src)

            if len(src) > seq_length:
                src = src[: seq_length]
                seg = seg[: seq_length]
                mask = mask[: seq_length]

            while len(src) < seq_length:
                src.append(0)
                seg.append(0)
                mask.append(0)
            dataset.append((src, seg, mask))

        data_cache_path = config['normal_data_cache_path']
        if not os.path.exists(os.path.dirname(data_cache_path)):
            os.makedirs(os.path.dirname(data_cache_path))
        with open(data_cache_path, 'wb') as f:
            pickle.dump(dataset, f)

    print("\n>>> loading sentences from {}, time cost:{:.2f}".
          format(config['test_path'], (time.time() - start) / 60.00))

    return dataset

def predict(dataset, pre_model, config):
    predict_logits, predictions = [], []
    p_logit = []

    src = torch.LongTensor([sample[0] for sample in dataset])
    seg = torch.LongTensor([sample[1] for sample in dataset])
    mask = torch.LongTensor([sample[2] for sample in dataset])

    for i, (src_batch, seg_batch, mask_batch) in \
            enumerate(batch_loader(config, src, seg, mask)):
        src_batch = src_batch.to(config['device'])
        seg_batch = seg_batch.to(config['device'])
        mask_batch = mask_batch.to(config['device'])
        with torch.no_grad():
            output = pre_model(input_ids=src_batch, token_type_ids=seg_batch, attention_mask=mask_batch)

        logits = output[0]
        logits = torch.softmax(logits, 1)
        p_logits = logits.cpu().numpy().tolist()
        for i in p_logits:
            p_logit.append(i)

    final_logit, predict_data = [], []
    for i in range(len(p_logit)):
        merge = []
        for j in range(len(p_logit[i])):
            tmp = p_logit[i][j]
            merge.append(tmp)
        final_logit.append(merge)
        res = argmax(merge)
        predictions.append(res)

    for i in final_logit:
        predict_data.append((i[0], i[1], i[2]))

    with open(config['output_logit_path'] + '.csv', 'w', newline='') as f:
        tsv_w = csv.writer(f, delimiter=',')
        tsv_w.writerow(['label0', 'label1', 'label2'])
        tsv_w.writerows(predict_data)

    if config['output_txt_path'] and config['output_txt_name'] is not None:
        if not os.path.exists(config['output_txt_path']):
            os.makedirs(config['output_txt_path'])
        out_txt = os.path.join(config['output_txt_path'], config['output_txt_name'])

        predict_data = []
        predict_k = 0

        # write results
        for i in predictions:
            predict_data.append((predict_k, i))
            predict_k += 1
        write2tsv(out_txt, predict_data)

    return predict_logits

def write(sent_list, path):
    with open(path, 'w', encoding='utf-8') as f:
        for i in sent_list:
            i = str(i)
            f.write(str(i) + '\n')

    print('\n>> result write done.')

def write2tsv(output_path, data):
    with open(output_path, 'w', newline='') as f:
        tsv_w = csv.writer(f, delimiter=',')
        tsv_w.writerow(['index', 'label'])
        tsv_w.writerows(data)

def generate_submit_result(config):
    reader = pd.read_csv(config['output_txt_path'] + config['output_txt_name'])
    label = reader['label'].tolist()
    k = 0
    all_ = []

    with open(config['origin_test_path'], 'r', encoding='utf-8') as f:
        for line_id, line in enumerate(f):
            line = line.strip()
            sent = json.loads(line)
            candidate = sent['candidate']
            for i in candidate:
                tgt = label[k]
                if tgt == 0:
                    i['label'] = config['label0']
                elif tgt == 1:
                    i['label'] = config['label1']
                elif tgt == 2:
                    i['label'] = config['label2']
                k += 1
            sent = json.dumps(sent, ensure_ascii=False)
            all_.append(sent)

    write(all_, config['submit_path'])
    print('\n>> submit file write done.')

def main():
    config = {
        'model_type': 'new_model',
        'output_logit_path': 'output_result/nezha',
        'normal_data_cache_path': '',
        'normal_r_data_cache_path': '',
        'vocab_path': '',
        'init_model_path': '',
        'origin_test_path': 'data/Xeon3NLP_round1_test_20210524.txt',
        'test_path': 'data/test.txt',
        'load_model_path': 'output_model/checkpoint-11865',
        'output_txt_path': 'output_result/',
        'output_txt_name': 'predict.csv',
        'submit_path': '../submit_ addr_match_runid.txt',
        'batch_size': 32,
        'max_seq_len': 128,
        'label0': '不匹配',
        'label1': '部分匹配',
        'label2': '完全匹配',
        'device': 'cuda',
    }

    warnings.filterwarnings('ignore')
    start_time = time.time()
    localtime_start = time.asctime(time.localtime(time.time()))
    print(">> program start at:{}".format(localtime_start))

    config['vocab_path'] = 'pretrain_code/' + config['model_type'] + '/vocab.txt'
    config['init_model_path'] = 'pretrain_code/' + config['model_type']
    config['normal_data_cache_path'] = 'user_data/processed/' + config['model_type'] + '/test_data.pkl'

    if not os.path.exists(config['normal_data_cache_path']):
        test_set, r_test_set = read_dataset(config)
    else:
        with open(config['normal_data_cache_path'], 'rb') as f:
            test_set = pickle.load(f)

    print("\n>> start predict ... ...")

    model = NeZhaSequenceClassification.from_pretrained(config['load_model_path'])
    model.to(config['device'])
    model.eval()

    predict(dataset=test_set, pre_model=model, config=config)

    generate_submit_result(config)

    localtime_end = time.asctime(time.localtime(time.time()))
    print("\n>> program end at : {}, total cost time : {:.2f}".
          format(localtime_end, (time.time() - start_time) / 60.00))

if __name__ == '__main__':
    main()