Compare commits
No commits in common. "1d22febc98df6d4aa6796efad5bfbf714dd7b485" and "b26863796e7275fe55e8e343e5416b450b4f2128" have entirely different histories.
1d22febc98
...
b26863796e
47 changed files with 0 additions and 269474 deletions
162
.gitignore
vendored
162
.gitignore
vendored
|
|
@ -1,162 +0,0 @@
|
|||
# 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/latest/usage/project/#working-with-version-control
|
||||
.pdm.toml
|
||||
.pdm-python
|
||||
.pdm-build/
|
||||
|
||||
# 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/
|
||||
53
chat.py
53
chat.py
|
|
@ -1,53 +0,0 @@
|
|||
"""A wrapper around LLMs, customized for our config"""
|
||||
|
||||
from gemini import Gemini
|
||||
from logging import getLogger
|
||||
from typing import Optional
|
||||
|
||||
|
||||
logger = getLogger()
|
||||
|
||||
|
||||
class Chat:
|
||||
"""Represents a configured interaction with an LLM"""
|
||||
|
||||
__GENAI_INITIALIZED = False
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.__system_message = None
|
||||
|
||||
def set_system_message(self, message: Optional[str]) -> None:
|
||||
"""Append a non-empty system message to initialize the chat."""
|
||||
self.__system_message = message
|
||||
|
||||
def chat(self, message: str) -> Optional[str]:
|
||||
"""Asks a question and returns the response.
|
||||
|
||||
Mostly used for debugging.
|
||||
The 'chat" method is usually more convenient.
|
||||
"""
|
||||
logger.debug("Sending chat to LLM: %s" % message)
|
||||
response = Gemini().chat(message, self.__system_message)
|
||||
if response.success:
|
||||
logger.debug('LLM responded: %s' % response.text_response)
|
||||
return response.text_response
|
||||
else:
|
||||
logger.error('LLM failed to respond.')
|
||||
return None
|
||||
|
||||
def smoke_test(self) -> str:
|
||||
""" Sends a test message to the LLM that we know the response to.
|
||||
|
||||
Returns:
|
||||
str: Error string if the test failed, empty string otherwise
|
||||
"""
|
||||
response = Gemini().chat('respond with "OK"', None)
|
||||
if response.success:
|
||||
if response.text_response == 'OK':
|
||||
return ''
|
||||
else:
|
||||
return ('Smoke test failed: llm was expected '
|
||||
'to return "OK", got: "%s"'
|
||||
% response.text_response)
|
||||
else:
|
||||
return 'LLM failed to respond'
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
Example word search
|
||||
Puzzle
|
||||
Hidden
|
||||
Search
|
||||
Words
|
||||
Letters
|
||||
Find
|
||||
Diagonal
|
||||
Vertical
|
||||
Horizontal
|
||||
Challenge
|
||||
Fun
|
||||
Brain
|
||||
Game
|
||||
Solve
|
||||
Clues
|
||||
Grid
|
||||
Focus
|
||||
Patience
|
||||
Victory
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +0,0 @@
|
|||
The settings in the Book Bolt Studio you picked are below, and the same must be used on KDP when creating your book
|
||||
|
||||
Create a book section:
|
||||
PAPERBACK
|
||||
|
||||
Step 2 of 3 - Paperback Content under Print Options:
|
||||
|
||||
BLACK AND WHITE INTERIOR WITH WHITE PAPER
|
||||
8.5 X 11
|
||||
BLEED
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 591 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
Before Width: | Height: | Size: 5.4 KiB |
|
|
@ -1,27 +0,0 @@
|
|||
Cinderella
|
||||
BALL
|
||||
SLIPPER
|
||||
STEPSISTERS
|
||||
PRINCE
|
||||
MAGIC
|
||||
FAIRY
|
||||
GODMOTHER
|
||||
CARRIAGE
|
||||
PUMPKIN
|
||||
MIDNIGHT
|
||||
GLASS
|
||||
KINDNESS
|
||||
POVERTY
|
||||
TRANSFORMATION
|
||||
LOVE
|
||||
BEAUTY
|
||||
AMBITION
|
||||
FORGIVENESS
|
||||
DREAM
|
||||
HOPE
|
||||
COURAGE
|
||||
HUMILITY
|
||||
WORK
|
||||
FAMILY
|
||||
INHERITANCE
|
||||
FATE
|
||||
Binary file not shown.
|
|
@ -1,27 +0,0 @@
|
|||
Goldilocks and the Three Bears
|
||||
GOLDILOCKS
|
||||
BEARS
|
||||
PORRIDGE
|
||||
CHAIR
|
||||
BED
|
||||
HOUSE
|
||||
FOREST
|
||||
BOWL
|
||||
TOO
|
||||
HOT
|
||||
COLD
|
||||
JUSTRIGHT
|
||||
LITTLE
|
||||
MIDDLE
|
||||
BIG
|
||||
PAPA
|
||||
MAMA
|
||||
BABY
|
||||
HAIR
|
||||
WANDER
|
||||
CURIOUS
|
||||
NAUGHTY
|
||||
LESSON
|
||||
FAMILY
|
||||
HOME
|
||||
ADVENTURE
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 287 KiB |
|
|
@ -1,27 +0,0 @@
|
|||
Hansel and Gretel
|
||||
FOREST
|
||||
GINGERBREAD
|
||||
WITCH
|
||||
CHILDREN
|
||||
BREADCRUMBS
|
||||
CANDY
|
||||
HOUSE
|
||||
OVEN
|
||||
SIBLINGS
|
||||
DANGER
|
||||
ESCAPE
|
||||
WOODS
|
||||
MAGICAL
|
||||
ADVENTURE
|
||||
EVIL
|
||||
TRAP
|
||||
FEAR
|
||||
COURAGE
|
||||
GREED
|
||||
CRUELTY
|
||||
LOVE
|
||||
HOME
|
||||
FAMILY
|
||||
SURVIVAL
|
||||
HOPE
|
||||
FREEDOM
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Jack and the Beanstalk
|
||||
JACK
|
||||
BEANSTALK
|
||||
GIANT
|
||||
MAGIC
|
||||
BEAN
|
||||
CLIMB
|
||||
CASTLE
|
||||
COW
|
||||
MONEY
|
||||
MOTHER
|
||||
AXE
|
||||
HARP
|
||||
GOOSE
|
||||
GOLDEN
|
||||
EGGS
|
||||
HEN
|
||||
GIANTESS
|
||||
WIFE
|
||||
ESCAPE
|
||||
ADVENTURE
|
||||
FAIRY
|
||||
TALE
|
||||
GREEDY
|
||||
CLEVER
|
||||
POOR
|
||||
MAGIC
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
|
|
@ -1,120 +0,0 @@
|
|||
from pypdf import PdfReader, PdfWriter, PageObject
|
||||
from pypdf.annotations import Link
|
||||
from reportlab.pdfgen import canvas
|
||||
from reportlab.lib.pagesizes import letter
|
||||
from PIL import Image
|
||||
import io
|
||||
|
||||
|
||||
def image_to_page(image_path: str,
|
||||
x_position: int, y_position: int,
|
||||
desired_width: int, desired_height: int) -> PageObject:
|
||||
image_pdf = io.BytesIO()
|
||||
c = canvas.Canvas(image_pdf, pagesize=letter)
|
||||
c.drawImage(image_path, x_position, y_position,
|
||||
width=desired_width, height=desired_height)
|
||||
c.save()
|
||||
image_pdf.seek(0)
|
||||
return PdfReader(image_pdf).pages[0]
|
||||
|
||||
|
||||
class Stamp:
|
||||
|
||||
def __init__(self,
|
||||
image_path: str,
|
||||
x_position: int, y_position: int,
|
||||
desired_width: int, desired_height: int) -> None:
|
||||
self._image_page = image_to_page(
|
||||
image_path, x_position, y_position, desired_width, desired_height)
|
||||
self._bounding_box = (x_position, y_position, x_position +
|
||||
desired_width - 1,
|
||||
y_position + desired_height - 1)
|
||||
|
||||
def stamp(self, page: PageObject) -> None:
|
||||
page.merge_page(self._image_page)
|
||||
|
||||
def add_link(self, writer: PdfWriter, i: int, j: int):
|
||||
annotation = Link(rect=self._bounding_box, target_page_index=j)
|
||||
writer.add_annotation(page_number=i, annotation=annotation)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
|
||||
writer = PdfWriter()
|
||||
|
||||
# Add a title page and "how to play"
|
||||
writer.add_page(image_to_page("grayscale-1.png", 25, 25, 550, 990))
|
||||
writer.add_page(
|
||||
PdfReader("Fairy tale puzzle book interior PRINT_VERSION.pdf").pages[0])
|
||||
|
||||
# Next, add the stamps to all puzzle pages
|
||||
reader = PdfReader("Fairy tale puzzle book interior PRINT_VERSION.pdf")
|
||||
stamp = Stamp("link_text.png", 495, 15, 100, 50)
|
||||
back_arrows = [
|
||||
Stamp("back.png", 278, 388, 20, 20),
|
||||
Stamp("back.png", 490, 388, 20, 20),
|
||||
Stamp("back.png", 278, 124, 20, 20),
|
||||
Stamp("back.png", 490, 124, 20, 20)
|
||||
]
|
||||
|
||||
# Add pages and merge the image to the specified page
|
||||
skips = [0, 1, 23]
|
||||
for i, page in enumerate(reader.pages):
|
||||
if i in skips:
|
||||
continue
|
||||
if i < 22:
|
||||
stamp.stamp(page)
|
||||
elif i > 22:
|
||||
for a in back_arrows:
|
||||
a.stamp(page)
|
||||
writer.add_page(page)
|
||||
|
||||
# Add forward and back annotations
|
||||
for i in range(2, 22):
|
||||
stamp.add_link(writer, i, 23 + (i - 2) // 4)
|
||||
for i in range(23, 28):
|
||||
offset = 4 * (i - 23) + 2
|
||||
for j in range(4):
|
||||
back_arrows[j].add_link(writer, i, offset + j)
|
||||
|
||||
# Add bookmarks
|
||||
writer.add_outline_item("Cover", 0)
|
||||
writer.add_outline_item("How to play", 1)
|
||||
parent = writer.add_outline_item("Puzzles", 2)
|
||||
for i, n in enumerate([
|
||||
"Cinderella",
|
||||
"Snow white",
|
||||
"The emperor's new clothes",
|
||||
"The Bremen town musicians",
|
||||
"Little red riding hood",
|
||||
"The three little pigs",
|
||||
"The princcess and the pea",
|
||||
"The ugly duckling",
|
||||
"The little mermaid",
|
||||
"Rumplestiltskin",
|
||||
"Sleeping beauty",
|
||||
"Hansel and Gretel",
|
||||
"Rapunzel",
|
||||
"The magic pen",
|
||||
"The talking animals",
|
||||
"Goldilocks and the three bears",
|
||||
"The gingerbread man",
|
||||
"The tortoise and the hare",
|
||||
"Jack and the beanstalk",
|
||||
"The frog prince",
|
||||
]):
|
||||
writer.add_outline_item(n, i + 2, parent)
|
||||
writer.add_outline_item("Solution", 22)
|
||||
|
||||
# Add copyright page
|
||||
writer.add_page(PdfReader('copyright.pdf').pages[0])
|
||||
writer.add_outline_item("Copyright", len(writer.pages) - 1)
|
||||
|
||||
# Write the output
|
||||
with open("Fairy tale puzzle book interior.pdf", "wb") as output_file:
|
||||
writer.write(output_file)
|
||||
print("DONE")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
Little Red Riding Hood
|
||||
RED
|
||||
HOOD
|
||||
FOREST
|
||||
WOLF
|
||||
GRANDMOTHER
|
||||
BASKET
|
||||
PATH
|
||||
WOODS
|
||||
CAPE
|
||||
BASKET
|
||||
TRAIL
|
||||
TRICKERY
|
||||
DECEPTION
|
||||
DANGER
|
||||
FEAR
|
||||
INNOCENCE
|
||||
GRANNY
|
||||
BASKET
|
||||
STORY
|
||||
FAIRY
|
||||
TALE
|
||||
MORAL
|
||||
LESSON
|
||||
EVIL
|
||||
GOOD
|
||||
Binary file not shown.
|
|
@ -1,27 +0,0 @@
|
|||
Rapunzel
|
||||
TOWER
|
||||
HAIR
|
||||
PRINCESS
|
||||
GOLDEN
|
||||
LONG
|
||||
TRAPPED
|
||||
WITCH
|
||||
MOTHER
|
||||
ESCAPE
|
||||
FLYNN
|
||||
RIDER
|
||||
KINGDOM
|
||||
LOVE
|
||||
FREEDOM
|
||||
ADVENTURE
|
||||
SUNLIGHT
|
||||
LANTERN
|
||||
SUNSHINE
|
||||
MAGIC
|
||||
HEALING
|
||||
BETRAYAL
|
||||
REDEMPTION
|
||||
COURAGE
|
||||
HOPE
|
||||
YOUTH
|
||||
BEAUTY
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
Rumpelstiltskin
|
||||
SPINNING
|
||||
STRAW
|
||||
GOLD
|
||||
GNOME
|
||||
GREED
|
||||
TRICKSTER
|
||||
MILLER
|
||||
DAUGHTER
|
||||
BARGAIN
|
||||
NAME
|
||||
POWER
|
||||
MAGIC
|
||||
DECEIT
|
||||
CUNNING
|
||||
FATE
|
||||
DESTINY
|
||||
BETRAYAL
|
||||
REDEMPTION
|
||||
FEAR
|
||||
LOVE
|
||||
SACRIFICE
|
||||
TRUTH
|
||||
CONSEQUENCE
|
||||
FREEDOM
|
||||
HOPE
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Sleeping Beauty
|
||||
SLEEP
|
||||
BEAUTY
|
||||
PRINCESS
|
||||
CURSE
|
||||
SPINDLE
|
||||
THORN
|
||||
PRINCE
|
||||
CASTLE
|
||||
SLUMBER
|
||||
DREAM
|
||||
FAIRY
|
||||
MAGIC
|
||||
LOVE
|
||||
KISS
|
||||
AWAKEN
|
||||
ENCHANTMENT
|
||||
FOREST
|
||||
CASTLE
|
||||
GOWN
|
||||
EVIL
|
||||
GOOD
|
||||
DRAGON
|
||||
DRAGON
|
||||
QUEEN
|
||||
KING
|
||||
LOVE
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
Snow White
|
||||
SNOW
|
||||
WHITE
|
||||
QUEEN
|
||||
MIRROR
|
||||
POISON
|
||||
APPLE
|
||||
DWARF
|
||||
FOREST
|
||||
PRINCE
|
||||
CASTLE
|
||||
MAGIC
|
||||
JEALOUSY
|
||||
BEAUTY
|
||||
EVIL
|
||||
HUNTSMAN
|
||||
WOODS
|
||||
COTTAGE
|
||||
SEVEN
|
||||
KIND
|
||||
GENTLE
|
||||
FAIR
|
||||
LOVE
|
||||
TRUE
|
||||
HAPPINESS
|
||||
RESCUE
|
||||
HOPE
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Bremen Town Musicians
|
||||
DONKEY
|
||||
DOG
|
||||
CAT
|
||||
ROOSTER
|
||||
BREMEN
|
||||
TOWN
|
||||
MUSICIANS
|
||||
JOURNEY
|
||||
FRIENDSHIP
|
||||
ADVENTURE
|
||||
OLD
|
||||
TIRED
|
||||
ABANDONED
|
||||
HOPE
|
||||
COURAGE
|
||||
MUSIC
|
||||
ROBBERS
|
||||
FEAR
|
||||
ESCAPE
|
||||
CLEVER
|
||||
CUNNING
|
||||
TRIUMPH
|
||||
LAUGHTER
|
||||
JOY
|
||||
CAMARADERIE
|
||||
RESILIENCE
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
The Emperor's New Clothes
|
||||
EMPEROR
|
||||
CLOTHES
|
||||
DECEPTION
|
||||
VANITY
|
||||
ARROGANCE
|
||||
WEAVERS
|
||||
INVISIBLE
|
||||
FRAUD
|
||||
CHILD
|
||||
TRUTH
|
||||
HONESTY
|
||||
NAKEDNESS
|
||||
FEAR
|
||||
CONFORMITY
|
||||
POWER
|
||||
SOCIETY
|
||||
FABRIC
|
||||
IMAGINATION
|
||||
GULLIBLE
|
||||
WISDOM
|
||||
CRITICISM
|
||||
SHAME
|
||||
AUTHORITY
|
||||
PERCEPTION
|
||||
ILLUSION
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Frog Prince
|
||||
FROG
|
||||
PRINCE
|
||||
PRINCESS
|
||||
KISS
|
||||
CURSE
|
||||
MAGIC
|
||||
POND
|
||||
CASTLE
|
||||
ROYAL
|
||||
TOAD
|
||||
TRANSFORMATION
|
||||
LOVE
|
||||
BEAUTY
|
||||
UGLY
|
||||
CHARM
|
||||
TRUST
|
||||
DECEPTION
|
||||
ADVENTURE
|
||||
COURAGE
|
||||
ROYALTY
|
||||
KINGDOM
|
||||
ENCHANTMENT
|
||||
FATE
|
||||
DESTINY
|
||||
CHOICE
|
||||
HOPE
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Gingerbread Man
|
||||
GINGERBREAD
|
||||
MAN
|
||||
RUN
|
||||
CHASE
|
||||
OVEN
|
||||
BAKE
|
||||
SWEET
|
||||
SPICY
|
||||
HOT
|
||||
FAST
|
||||
CUNNING
|
||||
CLEVER
|
||||
GINGERBREAD
|
||||
DELICIOUS
|
||||
GINGERBREAD
|
||||
CRUMBLY
|
||||
CRISPY
|
||||
GINGERBREAD
|
||||
BROWN
|
||||
GINGERBREAD
|
||||
GINGERBREAD
|
||||
ESCAPE
|
||||
GINGERBREAD
|
||||
GINGERBREAD
|
||||
CATCH
|
||||
GINGERBREAD
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
The Little Mermaid
|
||||
ARIEL
|
||||
OCEAN
|
||||
MERMAID
|
||||
PRINCESS
|
||||
SEA
|
||||
URSULA
|
||||
TRITON
|
||||
SEBASTIAN
|
||||
FLOUNDER
|
||||
SCUTTLE
|
||||
LOVE
|
||||
HUMAN
|
||||
DESIRE
|
||||
VOICE
|
||||
LEGS
|
||||
MAGIC
|
||||
ADVENTURE
|
||||
FORBIDDEN
|
||||
FAMILY
|
||||
SACRIFICE
|
||||
FREEDOM
|
||||
MUSIC
|
||||
SEAWEED
|
||||
CASTLE
|
||||
SHIP
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
The Magic Pen
|
||||
INK
|
||||
QUILL
|
||||
SPELL
|
||||
DREAM
|
||||
WORDS
|
||||
POWER
|
||||
STORY
|
||||
CREATION
|
||||
WONDER
|
||||
MYTH
|
||||
ENCHANT
|
||||
LEGEND
|
||||
INKWELL
|
||||
SCRIPT
|
||||
FANTASY
|
||||
IMAGINATION
|
||||
INSPIRATION
|
||||
SCRIBBLE
|
||||
STORYTELLER
|
||||
ETHEREAL
|
||||
UNLEASH
|
||||
ENCHANTMENT
|
||||
TRANSCENDENT
|
||||
BOUNDLESS
|
||||
ETERNAL
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Princess and the Pea
|
||||
PRINCESS
|
||||
PEA
|
||||
ROYAL
|
||||
TRUE
|
||||
LOVE
|
||||
BED
|
||||
MATTRESS
|
||||
SENSITIVITY
|
||||
GENTLE
|
||||
DELICATE
|
||||
PRINCE
|
||||
KINGDOM
|
||||
CASTLE
|
||||
CHARM
|
||||
ELEGANCE
|
||||
GRACE
|
||||
TEST
|
||||
AUTHENTICITY
|
||||
IDENTITY
|
||||
TRUTH
|
||||
NOBLE
|
||||
WORTHY
|
||||
LOVE
|
||||
MARRIAGE
|
||||
HAPPINESS
|
||||
STORY
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
The Talking Animals.
|
||||
ANIMAL
|
||||
SPEECH
|
||||
COMMUNICATION
|
||||
LANGUAGE
|
||||
BEASTS
|
||||
CREATURES
|
||||
FAUNA
|
||||
VOICES
|
||||
WHISPER
|
||||
ROAR
|
||||
BARK
|
||||
CHIRP
|
||||
MEOW
|
||||
HOWL
|
||||
HOOT
|
||||
SQUEAK
|
||||
CHATTER
|
||||
DIALOGUE
|
||||
CONVERSATION
|
||||
UNDERSTANDING
|
||||
WISDOM
|
||||
MYTH
|
||||
FABLE
|
||||
FANTASY
|
||||
REALITY
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Three Little Pigs
|
||||
PIG
|
||||
STRAW
|
||||
STICK
|
||||
BRICK
|
||||
WOLF
|
||||
HOUSE
|
||||
HUFF
|
||||
PUFF
|
||||
BLOW
|
||||
DOWN
|
||||
GREEDY
|
||||
CLEVER
|
||||
CUNNING
|
||||
SCARED
|
||||
SAFE
|
||||
SHELTER
|
||||
BROTHER
|
||||
SISTER
|
||||
FAMILY
|
||||
FOREST
|
||||
HUNGRY
|
||||
ESCAPE
|
||||
ADVENTURE
|
||||
LESSON
|
||||
PERSEVERANCE
|
||||
TEAMWORK
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
The Tortoise and the Hare
|
||||
TORTOISE
|
||||
HARE
|
||||
RACE
|
||||
SLOW
|
||||
FAST
|
||||
STEADY
|
||||
ARROGANT
|
||||
OVERCONFIDENT
|
||||
PERSEVERANCE
|
||||
DETERMINATION
|
||||
PATIENCE
|
||||
PRIDE
|
||||
HUMILITY
|
||||
LESSON
|
||||
AESOP
|
||||
FABLE
|
||||
COMPETITION
|
||||
WINNER
|
||||
LOSER
|
||||
SPEED
|
||||
AGILITY
|
||||
ENDURANCE
|
||||
CONFIDENCE
|
||||
EFFORT
|
||||
OUTCOME
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
The Ugly Duckling
|
||||
DUCKLING
|
||||
UGLY
|
||||
SWAN
|
||||
OUTCAST
|
||||
DIFFERENT
|
||||
ACCEPTANCE
|
||||
TRANSFORMATION
|
||||
SELFDOUBT
|
||||
BEAUTY
|
||||
GRACE
|
||||
FAMILY
|
||||
BELONGING
|
||||
JOURNEY
|
||||
RESILIENCE
|
||||
CONFIDENCE
|
||||
LOVE
|
||||
COMPASSION
|
||||
PREJUDICE
|
||||
KINDNESS
|
||||
HOPE
|
||||
CHANGE
|
||||
GROWTH
|
||||
IDENTITY
|
||||
FEAR
|
||||
COURAGE
|
||||
DESTINY
|
||||
118
gemini.py
118
gemini.py
|
|
@ -1,118 +0,0 @@
|
|||
"""A wrapper around gemini, customized for our config"""
|
||||
|
||||
from google.api_core.exceptions import ResourceExhausted
|
||||
from google.generativeai import (
|
||||
configure as genai_config, GenerationConfig, GenerativeModel)
|
||||
from google.generativeai.types.safety_types import HarmCategory
|
||||
from logging import getLogger
|
||||
from time import sleep
|
||||
from typedefs import Llm, ChatResult
|
||||
from typing import Optional
|
||||
|
||||
|
||||
logger = getLogger()
|
||||
|
||||
|
||||
class Gemini(Llm):
|
||||
"""Represents an interaction with Gemini"""
|
||||
|
||||
__GENAI_INITIALIZED = False
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
self.__model = None
|
||||
|
||||
def __create_payload(self,
|
||||
message: str,
|
||||
system_message: Optional[str] = None)\
|
||||
-> list[dict[str, str]]:
|
||||
"""Creates a list of messages we can send to an LLM."""
|
||||
assert message, 'Message must be non-empty'
|
||||
messages = []
|
||||
if system_message:
|
||||
messages.append(
|
||||
# TODO: in other LLMs, the role would be 'system'.
|
||||
# What's the best fit in Gemini?
|
||||
{'role': 'user', 'parts': [{'text': message}]})
|
||||
messages.append({'role': 'user', 'parts': [{'text': message}]})
|
||||
logger.debug('Message object to be sent:\n%s' % messages)
|
||||
return messages
|
||||
|
||||
def __get_model(self) -> GenerativeModel:
|
||||
if self.__model:
|
||||
return self.__model
|
||||
if not Gemini.__GENAI_INITIALIZED:
|
||||
logger.debug('Calling genai_config()')
|
||||
genai_config()
|
||||
Gemini.__GENAI_INITIALIZED = True
|
||||
gen_config = GenerationConfig(temperature=1.0)
|
||||
self.__model = GenerativeModel('gemini-1.5-flash-latest',
|
||||
generation_config=gen_config,
|
||||
)
|
||||
return self.__model
|
||||
|
||||
def __count_words(self, payload: list[dict[str, any]]) -> int:
|
||||
count = 0
|
||||
for i in payload:
|
||||
if 'parts' not in i:
|
||||
continue
|
||||
for p in i['parts']:
|
||||
if 'text' not in p:
|
||||
continue
|
||||
count += len(p['text'].split())
|
||||
return count
|
||||
|
||||
def chat(
|
||||
self,
|
||||
message: str,
|
||||
system_message: Optional[str])\
|
||||
-> ChatResult:
|
||||
payload = self.__create_payload(message, system_message)
|
||||
# see https://www.googlecloudcommunity.com/\
|
||||
# gc/AI-ML/Gemini-Pro-Quota-Exceeded/m-p/693185
|
||||
sleep_count = 0
|
||||
sleep_time = 2
|
||||
words_sent = 0
|
||||
words_received = 0
|
||||
payload_wordcount = self.__count_words(payload)
|
||||
while True:
|
||||
try:
|
||||
words_sent += payload_wordcount
|
||||
response = self.__get_model().generate_content(payload)
|
||||
if response and response.text:
|
||||
words_received += len(response.text.split())
|
||||
except ValueError as ve:
|
||||
logger.warn("ValueError occurred, skipping topic: %s" % ve)
|
||||
return ChatResult(
|
||||
success=False,
|
||||
text_response=None,
|
||||
words_sent=words_sent,
|
||||
words_received=words_received
|
||||
)
|
||||
except ResourceExhausted as re:
|
||||
logger.warn(
|
||||
'ResourceExhausted exception occurred '
|
||||
'while talking to gemini: %s' % re)
|
||||
sleep_count += 1
|
||||
if sleep_count > 5:
|
||||
logger.warn(
|
||||
'ResourceExhausted exception occurred '
|
||||
'5 times in a row. Exiting.')
|
||||
return ChatResult(
|
||||
success=False,
|
||||
text_response=None,
|
||||
words_sent=words_sent,
|
||||
words_received=words_received
|
||||
)
|
||||
logger.info(
|
||||
'Too many requests, backing off for %s seconds'
|
||||
% sleep_time)
|
||||
sleep(sleep_time)
|
||||
sleep_time *= 2
|
||||
else:
|
||||
return ChatResult(
|
||||
success=True,
|
||||
text_response=response.text.rstrip(),
|
||||
words_sent=words_sent,
|
||||
words_received=words_received
|
||||
)
|
||||
37
generator.py
37
generator.py
|
|
@ -1,37 +0,0 @@
|
|||
"""Generates content"""
|
||||
from chat import Chat
|
||||
from util import dedupe
|
||||
|
||||
|
||||
class Generator():
|
||||
|
||||
def suggest_topics(self, theme: str, amount: int) -> list[str]:
|
||||
chat = Chat()
|
||||
chat.set_system_message(
|
||||
'You are assisting a puzzle creator in generating '
|
||||
'ideas for topics for word search puzzles.'
|
||||
'Each topic should be unique and match a common theme.'
|
||||
)
|
||||
suggestions = chat.chat(
|
||||
'Suggest %s topics for the theme \"%s\". ' % (amount, theme)
|
||||
+ 'Return only the suggested words as a comma-separated list.'
|
||||
)
|
||||
suggestions = [s.strip() for s in suggestions.split(',')]
|
||||
return dedupe(suggestions)
|
||||
|
||||
def suggest_words(self, topic: str, amount: int) -> list[str]:
|
||||
chat = Chat()
|
||||
chat.set_system_message(
|
||||
'You are assisting a puzzle creator in generating '
|
||||
'sets of words to be used in word searches.'
|
||||
'Each word must be 14 characters or less.'
|
||||
'Words may not repeat. Each suggestion must be '
|
||||
'only a single word.'
|
||||
)
|
||||
suggestions = chat.chat(
|
||||
'Suggest %s single words for the topic \"%s\". '
|
||||
'Return only the words as a comma-separated list.'
|
||||
% (amount, topic)).split(',')
|
||||
if suggestions:
|
||||
return dedupe([s.strip() for s in suggestions])
|
||||
return []
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
PREFIX=$(echo $1 | sed 's/.pdf//')
|
||||
TMPDIR=$(mktemp -d)
|
||||
MYDIR=$PWD
|
||||
cd $TMPDIR
|
||||
cp $MYDIR/$PREFIX.pdf $TMPDIR/
|
||||
pdftoppm -png -x 90 -y 100 -W 1120 -H 1500 $PREFIX.pdf $PREFIX
|
||||
rm $PREFIX.pdf
|
||||
for file in *
|
||||
do
|
||||
CMD="/usr/bin/convert $file -transparent white $file"
|
||||
eval $CMD
|
||||
done
|
||||
cp * $MYDIR/
|
||||
cd $MYDIR
|
||||
rm -rf $TMPDIR
|
||||
22
typedefs.py
22
typedefs.py
|
|
@ -1,22 +0,0 @@
|
|||
"""Type definitions"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
class ChatResult:
|
||||
success: bool
|
||||
text_response: str
|
||||
# TODO: switch stats to tokens instead of words!!!
|
||||
words_sent: int
|
||||
words_received: int
|
||||
|
||||
|
||||
class Llm:
|
||||
def chat(
|
||||
self,
|
||||
message: str,
|
||||
system_message: Optional[str])\
|
||||
-> ChatResult:
|
||||
pass
|
||||
34
util.py
34
util.py
|
|
@ -1,34 +0,0 @@
|
|||
"""Utilities"""
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def letters_only(s: str) -> str:
|
||||
return re.compile('[^a-zA-Z]').sub('', s)
|
||||
|
||||
|
||||
def words_only(s: str) -> str:
|
||||
words = [letters_only(x) for x in s.split()]
|
||||
r = []
|
||||
for w in words:
|
||||
if w:
|
||||
r.append(w)
|
||||
return ' '.join(r)
|
||||
|
||||
|
||||
def file_name(s: str, ext='txt') -> str:
|
||||
return '%s.%s' % (s.lower().replace(' ', '_'), ext)
|
||||
|
||||
|
||||
def dedupe(strings: list[str]) -> list[str]:
|
||||
seen = set()
|
||||
r = []
|
||||
for s in strings:
|
||||
if s not in seen:
|
||||
r.append(s)
|
||||
seen.add(s)
|
||||
return r
|
||||
|
||||
|
||||
def header(s: str) -> str:
|
||||
return words_only(s).lower().title()
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
from generator import Generator
|
||||
from util import letters_only, header
|
||||
import dotenv
|
||||
import logging
|
||||
import random
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
dotenv.load_dotenv()
|
||||
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
||||
lists = []
|
||||
for topic in Generator().suggest_topics(
|
||||
'Events, activities, and phenomena in winter',
|
||||
105):
|
||||
logger.info('Creating topic for "%s".', topic)
|
||||
generated = Generator().suggest_words(topic, 24)
|
||||
if generated and len(generated) >= 10:
|
||||
lists.append((topic, generated))
|
||||
random.shuffle(lists)
|
||||
logger.info('Writing %s lists to output' % len(lists))
|
||||
output = []
|
||||
for topic, generated in lists:
|
||||
output.append(header(topic))
|
||||
output.extend([letters_only(c).upper() for c in generated])
|
||||
output.append('')
|
||||
output.pop() # remove last newline
|
||||
print('\n'.join(output))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
import dotenv
|
||||
import logging
|
||||
from pysuchsel.Alphabet import Alphabet
|
||||
from pysuchsel.Suchsel import Suchsel
|
||||
from pysuchsel.RandomDist import RandomDist
|
||||
from util import file_name
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
dotenv.load_dotenv()
|
||||
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
||||
|
||||
plcrule = RandomDist({
|
||||
"lr": 1,
|
||||
"tb": 1,
|
||||
})
|
||||
|
||||
# w: 1275
|
||||
# h: 1651
|
||||
suchsel = Suchsel(10, 10, plcrule, 5)
|
||||
words = ['TOWER', 'HAIR', 'PRINCESS', 'GOLDEN', 'RAPUNZEL']
|
||||
unplaced = []
|
||||
for word in words:
|
||||
placed = suchsel.place(word, contiguous=False)
|
||||
if not placed:
|
||||
unplaced.append(word)
|
||||
|
||||
# TODO: optimize
|
||||
print('UNPLACE: %s' % unplaced)
|
||||
|
||||
filler = Alphabet('en')
|
||||
suchsel.fill(filler)
|
||||
suchsel.write_svg(file_name('tangled', 'svg'))
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Add table
Reference in a new issue