INTRODUCTION AND MOTIVATION
Welcome, future innovators and creative minds! Imagine a world where learning to code is as easy and fun as drawing a picture or telling a story. For children aged six and up, traditional programming languages can sometimes feel like a puzzle with too many complicated pieces. That is why we are thrilled to introduce COOL, a "Coding for Our Little ones" language, designed from the ground up to be incredibly intuitive, visually engaging, and wonderfully simple. COOL empowers young learners to bring their ideas to life with graphics, logic, and even a touch of artificial intelligence, all while building a strong foundation in computational thinking.
Now, imagine your programs having their own characters that can think and act *at the same time*! With the new "Agents" feature in COOL, your code can create smart, independent entities that remember things, do actions, and even draw themselves on the screen. The coolest part? These Agents can now work together, each doing their own thing, making your programs feel much more alive and dynamic. This makes COOL even more like playing with digital building blocks, allowing you to create interactive stories, games, and dynamic art like never before.
This article will take you on a journey through the COOL language, exploring its design principles, its easy-to-use features, and the clever "transpiler" that converts COOL code into standard Python, allowing it to run on almost any computer. We will also dive into practical examples, showing how COOL makes complex ideas simple and engaging for our youngest programmers, especially with the power of Agents that can run in parallel!
1. Introducing COOL: Coding Made Playful and Simple
The core philosophy behind COOL is to remove barriers and amplify creativity. We understand that for young children, abstract concepts can be challenging. Therefore, COOL emphasizes visual results, straightforward commands, and a syntax that mirrors natural language as closely as possible. This approach allows children to focus on what they want to create rather than getting bogged down by complex syntax rules or cryptic error messages.
COOL is built on several key design principles:
- Simplicity and Readability: Every command in COOL is designed to be easy to understand and remember, often using single, descriptive words. The language avoids punctuation and complex structures, opting instead for clear, direct instructions.
- Visual First: Graphics are not an afterthought in COOL; they are a central component. Children can draw shapes, change colors, and move objects on a screen with simple commands, providing immediate and satisfying visual feedback for their code.
- Intuitive Logic: Conditional statements (like "if this happens, then do that") and loops (like "do this many times") are expressed in a way that mirrors everyday thinking, making logical flow easy to grasp.
- Empowering Interaction: COOL includes simple commands for user input and output, enabling children to create interactive programs and stories.
- Smart, Parallel Agents for Dynamic Programs: This new and exciting feature allows you to create independent "characters" or "objects" within your program that can *act at the same time*. These Agents can remember their own information (like their color or size) and perform their own actions (like moving or drawing themselves). You can tell them what to do, and they will get to it when they can, without waiting for other Agents. This makes programs much more dynamic and fun to build!
- Future-Ready with LLM Integration: Recognizing the growing importance of artificial intelligence, COOL offers a remarkably simple way for children to interact with large language models (LLMs), allowing them to ask questions and get answers directly within their programs. This opens up a world of possibilities for educational games, interactive learning, and creative storytelling.
- Safe and Controlled Environment: While integrating powerful tools like LLMs, COOL ensures that these interactions are managed within a safe and age-appropriate framework, focusing on educational and creative uses.
2. The COOL Language: Syntax and Features
Let us explore the specific elements that make COOL such an approachable and powerful tool for young programmers. We will use small code snippets to illustrate each feature, building towards a complete example.
2.1. Basic Program Structure
Every COOL program begins with the keyword `START` and concludes with the keyword `FINISH`. This clear demarcation helps children understand the boundaries of their program. All the instructions they write will be placed between these two keywords.
Example: A First COOL Program
START
SAY "Hello, COOL World!"
FINISH
In this simple example, the program starts, displays a friendly message to the user, and then gracefully concludes. The `SAY` command is our basic tool for showing text on the screen.
2.2. Variables and Data Types
Variables are like named boxes where we can store information. In COOL, we can store different kinds of information, which we call data types. COOL supports four fundamental data types that are easy for children to understand:
- NUMBER: This type is for whole numbers or numbers with decimals, like 7, 100, or 3.14.
- TEXT: This type is for words, sentences, or any sequence of characters, always enclosed in double quotes, like "Hello" or "My name is Lily".
- TRUTH: This type is for values that are either `TRUE` or `FALSE`, which are very useful for making decisions in our programs.
- SHAPE: This special type represents a graphic object that we can draw on the screen, such as a circle or a square.
To create a variable and give it a value, we use the `REMEMBER` keyword, followed by the variable's name, the keyword `IS`, and then the value.
Example: Storing Information
START
REMEMBER my_age IS 7
REMEMBER my_name IS "Lily"
REMEMBER is_fun IS TRUE
SAY "My name is " AND my_name AND " and I am " AND my_age AND " years old."
SAY "Is programming fun? " AND is_fun
FINISH
Here, we declare three variables: `my_age` stores a number, `my_name` stores some text, and `is_fun` stores a truth value. Notice how we can combine text and variables using the word `AND` to create a longer message for the `SAY` command.
2.3. Output and Input
Interacting with the user is a fundamental part of many programs. COOL provides simple commands for displaying messages and receiving input from the user.
- SAY [message/variable]: As seen before, this command displays text or the value of a variable on the screen.
- LISTEN FOR [variable_name] AS [TYPE]: This command pauses the program and waits for the user to type something and press Enter. Whatever the user types is then stored in the specified variable and converted to the specified type (`TEXT`, `NUMBER`, or `TRUTH`).
Example: An Interactive Greeting
START
SAY "Hello, what is your name?"
REMEMBER user_name IS "Guest"
LISTEN FOR user_name AS TEXT
SAY "Nice to meet you, " AND user_name AND "!"
SAY "How old are you?"
REMEMBER user_age IS 0
LISTEN FOR user_age AS NUMBER
SAY user_name AND ", you are " AND user_age AND " years old. That's COOL!"
FINISH
In this program, the computer asks for the user's name and age, and then uses that information to respond in a personalized way. This simple interaction makes the program feel much more alive and engaging.
2.4. Graphics Commands
One of COOL's most exciting features is its integrated graphics capabilities. Children can draw shapes, set colors, and move objects on a virtual canvas with ease. The graphics system works like drawing on a piece of paper, where you can pick a color and then draw a shape at a specific spot.
- CANVAS SIZE [width] [height]: This command creates the drawing area, specifying its width and height in pixels. The center of this canvas is always (0,0), making it easy to place objects.
- COLOR [color_name]: This command sets the current drawing color. Any subsequent shapes drawn will use this color. Common colors like `RED`, `BLUE`, `GREEN`, `YELLOW`, `PURPLE`, `ORANGE`, `BLACK`, `WHITE` are supported.
- DRAW SQUARE AT [x] [y] SIZE [s]: This command draws a square. `x` and `y` define the center position of the square, and `s` defines its side length.
- DRAW CIRCLE AT [x] [y] SIZE [r]: This command draws a circle. `x` and `y` define the center position of the circle, and `r` defines its radius.
- MOVE TO [x] [y]: This command moves the "drawing pen" to a new position without drawing anything.
- PEN DOWN: Starts drawing a line if the pen moves.
- PEN UP: Stops drawing a line if the pen moves.
- TURN [angle]: Rotates the "drawing pen" by a specified angle (in degrees).
- FORWARD [distance]: Moves the "drawing pen" forward by a specified distance, drawing a line if the pen is down.
- CLEAR SCREEN: Erases everything currently drawn on the canvas. This is very useful for animation, as you can clear the old picture before drawing the new one.
- WAIT [seconds] SECONDS: Pauses the program for a short amount of time, allowing you to control the speed of animations or game updates.
Example: Drawing a Simple House
START
CANVAS SIZE 400 300 -- Set up a drawing area 400 pixels wide, 300 pixels high
COLOR BLUE -- Choose blue for the walls
DRAW SQUARE AT 0 0 SIZE 100 -- Draw a square for the house body, centered
COLOR RED -- Choose red for the roof
MOVE TO -50 50 -- Move to the top-left corner of the square body
PEN DOWN -- Start drawing
TURN 45 -- Turn to draw the first side of the roof
FORWARD 70.7 -- Draw the first side (approximate length for a 45-degree angle)
TURN 90 -- Turn for the second side
FORWARD 70.7 -- Draw the second side
PEN UP -- Stop drawing
COLOR YELLOW -- Choose yellow for the window
DRAW SQUARE AT 0 -20 SIZE 20 -- Draw a small square for a window inside the house
FINISH
This example demonstrates how easy it is to create visual elements. The `--` symbol indicates a comment, which is a note for humans and is ignored by the computer. Comments are very helpful for explaining what the code does.
2.5. Conditional Logic (IF/ELSE IF/ELSE)
Making decisions is a crucial part of programming. COOL uses `IF`, `ELSE IF`, and `ELSE` statements to allow programs to behave differently based on certain conditions. This helps children understand cause and effect.
- IF [condition] THEN
- [code to run if condition is true]
- ELSE IF [another_condition] THEN
- [code to run if another_condition is true]
- ELSE
- [code to run if no condition is true]
- END IF
Conditions can compare values using simple phrases:
- `IS EQUAL TO`
- `IS GREATER THAN`
- `IS LESS THAN`
Example: Checking a Secret Number
START
REMEMBER secret_number IS 5
SAY "Guess my secret number (between 1 and 10):"
REMEMBER user_guess IS 0
LISTEN FOR user_guess AS NUMBER
IF user_guess IS EQUAL TO secret_number THEN
SAY "Wow! You guessed it right!"
ELSE IF user_guess IS GREATER THAN secret_number THEN
SAY "Too high! Try a smaller number."
ELSE
SAY "Too low! Try a bigger number."
END IF
FINISH
Here, the program asks the user to guess a number. It then compares the user's guess to the `secret_number`. If they match, a congratulatory message is shown; otherwise, a hint is given whether the guess was too high or too low.
2.6. Loops (LOOP ... TIMES DO, LOOP FOREVER DO)
Sometimes, we want the computer to repeat an action multiple times. Loops are perfect for this! COOL provides two simple loop structures:
- LOOP [number] TIMES DO
- [code to repeat]
- END LOOP
This loop repeats a specific number of times.
- LOOP FOREVER DO
- [code to repeat endlessly]
- BREAK LOOP -- Use this command to stop an endless loop based on a condition
- END LOOP
This loop repeats without end until a `BREAK LOOP` command is encountered, often inside an `IF` statement.
Example: Drawing a Row of Circles
START
CANVAS SIZE 300 150
COLOR GREEN
REMEMBER x_position IS -100
LOOP 4 TIMES DO
DRAW CIRCLE AT x_position 0 SIZE 20
REMEMBER x_position IS x_position PLUS 60 -- Move to the right for the next circle
END LOOP
FINISH
This program draws four green circles in a row. Each time the loop runs, a circle is drawn, and then the `x_position` variable is updated to move the next circle further to the right. This demonstrates how loops can be used with variables to create patterns efficiently.
2.7. Agents: Bringing Your Ideas to Life with Parallel Actions!
This is where COOL gets really exciting! Agents are like smart characters or objects in your program that can now act independently and seem to work at the same time. They can remember their own "stuff" (data/properties) and have their own "things they can do" (functions/methods). You can talk to them by sending them "messages," and they will perform their actions when they get a chance.
Imagine your Agents are all little robots in a room. When you `TELL` them something, you're writing a note and putting it in their mailbox. They'll read it and do it when they get a chance. And every time the game "ticks" or updates, all the robots get a chance to `UPDATE` themselves, decide what to do, and then show you where they are by `DRAW`ing themselves. This makes them feel like they're all moving and thinking at the same time!
- Defining an Agent Type: First, you describe what kind of Agent you want to make. This is like designing a blueprint for a robot.
MAKE AGENT TYPE [AgentTypeName]
REMEMBER [property_name] IS [initial_value]
-- Agents can have their own data, like a color or a name.
TO [FunctionName] DO
-- Agents can have their own actions (functions).
-- Inside a function, you can use the Agent's properties
-- by saying `MY [property_name]`.
END FUNCTION
-- Special: The UPDATE function is where the agent "thinks" and acts.
TO UPDATE DO
-- This function is called regularly by the main program.
-- It's where your agent's independent actions happen.
-- After this code runs, the agent will automatically DRAW itself!
END FUNCTION
-- If your Agent is a visual shape, it must have a special DRAW function.
TO DRAW DO
-- This function tells the Agent how to draw itself.
-- It will use COOL's graphics commands.
END FUNCTION
END AGENT TYPE
- Creating an Agent: Once you have a blueprint, you can make as many Agents of that type as you want.
CREATE [agent_name] AS [AgentTypeName]
- Talking to Agents (Sending Messages):
You tell an Agent what to do by sending it a message, which is like calling one of its functions. When you `TELL` an Agent, the message is queued, and the Agent will process it when it gets a chance.
TELL [agent_name] TO [FunctionName]
TELL [agent_name] TO [FunctionName] WITH [value1, value2, ...]
-- If a function needs more information, you send it `WITH` values.
TELL MYSELF TO [FunctionName]
-- An agent can even send a message to itself!
- Managing All Agents in the Main Program Loop: To make all your Agents feel like they are running "in parallel," your main program loop will use two special commands:
- PROCESS ALL AGENT MESSAGES: This command tells the system to check all Agents' mailboxes and make them perform any actions they were `TELL`ed to do.
- UPDATE ALL AGENTS: This command tells every active Agent to run its `TO UPDATE DO` function. Remember, after `UPDATE`, the Agent automatically `DRAW`s itself!
Example: A Simple Talking Robot Agent
START
MAKE AGENT TYPE TalkerRobot
REMEMBER my_name IS "Robbie"
REMEMBER my_mood IS "happy"
TO SayHello DO
SAY "Hello! My name is " AND MY my_name AND " and I am feeling " AND MY my_mood AND "."
END FUNCTION
TO ChangeMood WITH new_mood DO
REMEMBER MY my_mood IS new_mood
SAY MY my_name AND " is now feeling " AND MY my_mood AND "."
END FUNCTION
TO UPDATE DO
-- Robbie doesn't do much on his own, but he will draw himself if he were a shape.
-- For now, he just updates his internal state (which doesn't change here).
-- The DRAW call is automatically added after this block by the transpiler.
END FUNCTION
END AGENT TYPE
CREATE my_first_robot AS TalkerRobot
CREATE my_second_robot AS TalkerRobot
REMEMBER MY my_second_robot my_name IS "Cathy"
REMEMBER MY my_second_robot my_mood IS "curious"
TELL my_first_robot TO SayHello
TELL my_second_robot TO SayHello
TELL my_first_robot TO ChangeMood WITH "excited"
TELL my_second_robot TO ChangeMood WITH "playful"
-- We need to process messages for them to act!
PROCESS ALL AGENT MESSAGES
PROCESS ALL AGENT MESSAGES -- Process again to see the mood changes
SAY "All robots have processed their messages!"
FINISH
In this example, we define a `TalkerRobot` type. Each `TalkerRobot` remembers its name and mood. It has functions to `SayHello` and `ChangeMood`. We then create two robots and `TELL` them to perform actions. The `PROCESS ALL AGENT MESSAGES` command is crucial here; it makes the robots actually do what they were told. Notice `MY my_name` and `MY my_mood` when referring to the Agent's own data.
- Geometric Agents (Agents that Draw Themselves): If your Agent is a shape that can be drawn on the screen, you must give it a special function called `DRAW`. When the main program tells all Agents to `UPDATE`, your geometric Agent's `TO UPDATE DO` function will run, and then it will automatically call its `DRAW` function to show itself.
Example: A Moving Circle Agent
START
CANVAS SIZE 500 300
MAKE AGENT TYPE MovingCircle
REMEMBER my_x IS 0
REMEMBER my_y IS 0
REMEMBER my_size IS 30
REMEMBER my_color IS "blue"
REMEMBER my_speed IS 10
TO DRAW DO
COLOR MY my_color
DRAW CIRCLE AT MY my_x MY my_y SIZE MY my_size
END FUNCTION
TO MoveRight DO
REMEMBER MY my_x IS MY my_x PLUS MY my_speed
END FUNCTION
TO UPDATE DO
-- This agent moves right on its own during each update cycle
TELL MYSELF TO MoveRight -- Agent tells itself to move
-- The DRAW call is automatically added after this block by the transpiler.
END FUNCTION
END AGENT TYPE
CREATE my_dot AS MovingCircle
CREATE another_dot AS MovingCircle
REMEMBER MY another_dot my_x IS -100
REMEMBER MY another_dot my_color IS "red"
REMEMBER MY another_dot my_speed IS 5
LOOP 20 TIMES DO
CLEAR SCREEN -- Erase everything from the previous frame
PROCESS ALL AGENT MESSAGES -- Let agents process any queued instructions
UPDATE ALL AGENTS -- Tell all agents to update and draw themselves (and then draw themselves)
WAIT 0.1 SECONDS -- Pause for a moment to see the movement
END LOOP
FINISH
This `MovingCircle` Agent remembers its position, size, color, and speed. Its `DRAW` function uses these properties to draw a circle. Its `UPDATE` function tells itself to `MoveRight`. We create two `MovingCircle` agents, give them different starting positions and speeds, and then in a loop, we `CLEAR SCREEN`, `PROCESS ALL AGENT MESSAGES`, and `UPDATE ALL AGENTS`. This makes both circles appear to move across the screen at their own speeds, independently! Notice `TELL MYSELF TO MoveRight` for an agent to call its own function.
2.8. LLM Integration
One of the most innovative features of COOL is its direct and simple way to integrate with Large Language Models (LLMs), often referred to as "AI brains." This allows children to ask questions and receive intelligent responses within their programs, opening up possibilities for educational tools, interactive stories, and creative exploration.
- ASK BRAIN [question_text] AND GET ANSWER INTO [variable_name]
This command sends a question (as text) to the LLM and stores the LLM's response (also as text) into a specified variable.
Example: Asking the "Brain" a Question
START
SAY "I can ask a smart brain anything! What do you want to know?"
REMEMBER user_question IS ""
LISTEN FOR user_question AS TEXT
SAY "Thinking..."
REMEMBER brain_answer IS ""
ASK BRAIN user_question AND GET ANSWER INTO brain_answer
SAY "The brain says: " AND brain_answer
FINISH
In this example, the program prompts the user for a question. It then sends that question to the "brain" (the LLM) and prints the received answer. This feature makes COOL incredibly powerful for learning and discovery, allowing children to tap into vast amounts of information in a conversational way. It is important to note that the actual LLM interaction happens through a secure, pre-configured interface, ensuring age-appropriate content and usage.
3. The COOL Transpiler: Bringing COOL to Life with Python
COOL programs are not run directly by the computer. Instead, they are transformed into another programming language that computers already understand very well: Python. This transformation process is handled by a special program called the COOL Transpiler.
3.1. What is a Transpiler?
A transpiler is like a translator. It takes code written in one programming language (COOL) and converts it into equivalent code in another programming language (Python). The great thing about using a transpiler is that it allows us to design COOL to be super simple for kids, while still leveraging the power and widespread availability of Python. Once the COOL code is "transpiled" into Python, a standard Python interpreter (the program that runs Python code) can then execute it.
3.2. How the COOL Transpiler Works (Conceptually)
The COOL Transpiler is a Python program itself. When you give it a COOL file, it performs a series of steps:
- Reading the COOL Code: The transpiler first reads the COOL program line by line, or by breaking it down into smaller meaningful pieces called "tokens."
- Understanding the COOL Code: It then tries to understand the meaning of each COOL command, much like understanding sentences in English. It identifies keywords like `SAY`, `REMEMBER`, `DRAW`, `IF`, `LOOP`, and the values or conditions associated with them. Crucially, it now also understands the structure of `AGENT TYPE` definitions, recognizing their properties and functions, and the special `TO UPDATE DO` method.
- Generating Python Code: For each COOL command, the transpiler writes the equivalent Python code.
- `SAY "Hello"` becomes `print("Hello")`.
- `REMEMBER my_age IS 7` becomes `my_age = 7`.
- `MAKE AGENT TYPE Robot` becomes `class Robot(cool_agents.BaseAgent):`.
- `REMEMBER MY color IS "red"` inside an Agent becomes `self.color = "red"`.
- `TO Move DO` inside an Agent becomes `def move(self):`.
- `TO UPDATE DO` inside an Agent becomes `def update(self):` followed by the user's update logic, and then the transpiler *automatically adds* `self._post_update_draw()`, which then calls `self.draw()` if it exists. This is the secret to Agents drawing themselves after updating!
- `CREATE my_robot AS Robot` becomes `my_robot = Robot()`.
- `TELL my_robot TO Move` becomes `my_robot._send_message("move")`. This queues a message for the robot to move, letting it happen asynchronously.
- `TELL MYSELF TO Move` becomes `self._send_message("move")`.
- `PROCESS ALL AGENT MESSAGES` becomes `cool_agents.process_all_agent_messages()`.
- `UPDATE ALL AGENTS` becomes `cool_agents.update_all_agents()`.
- `CLEAR SCREEN` becomes `cool_graphics.clear_screen()`.
- `WAIT 0.1 SECONDS` becomes `time.sleep(0.1)`.
The transpiler uses three helper Python modules: `cool_graphics.py` for all drawing functions, `cool_llm_interface.py` for talking to the Smart Brain, and the new `cool_agents.py` to manage all the Agents and their messages.
- Creating a Python File: All the generated Python code is then saved into a new file, typically with a `.py` extension.
- Running the Python File: Finally, the transpiler can automatically tell the computer to run this newly created Python file using the Python interpreter. This is how your COOL program comes to life!
The transpiler cleverly handles all these conversions, abstracting away the details of Python and the complexities of making Agents run "in parallel" from the child. The child only needs to learn the simple, friendly COOL language and how to `TELL` and `UPDATE` their Agents.
4. Getting Started with COOL
To use COOL, a child would typically follow these steps:
1. Write COOL Code: Using a simple text editor (like Notepad on Windows or TextEdit on Mac, or even a specialized COOL editor if one were developed), the child types their COOL program and saves it as a file, for example, `my_agent_story.cool`.
2. Run the Transpiler: An adult or teacher would then use a command on the computer to run the COOL Transpiler, pointing it to the `my_agent_story.cool` file. For example, `python cool_transpiler.py my_agent_story.cool`.
3. Watch it Go!: The transpiler will then create the Python code and execute it. If the COOL program involves graphics, a new window will pop up showing the child's drawing or animation. If it involves text interaction, the program will prompt the user in the command window.
This process keeps the technical details of Python hidden, allowing children to focus purely on the creative and logical aspects of programming in COOL.
5. Conclusion: The Future is COOL!
COOL is more than just another programming language; it is a gateway to computational thinking, creativity, and problem-solving for the youngest generation. By offering a super simple, visually engaging, and highly interactive environment, COOL makes the complex world of programming accessible and fun for children aged six and up.
With its natural language-like syntax, integrated graphics, straightforward logic, innovative LLM integration, and now the power of Agents that can act "in parallel," COOL empowers children to become creators rather than just consumers of technology. It fosters curiosity, encourages experimentation, and builds confidence, setting them on a path to understand and shape the digital world around them. We believe that with COOL, every child can discover the joy and power of coding.
ADDENDUM: FULL RUNNING EXAMPLE - THE AGENT ADVENTURE
This addendum provides a complete COOL program, its transpiled Python equivalent, and the necessary Python helper modules. This example creates an interactive program featuring two types of Agents: a `PlayerAgent` and a `TreasureAgent`. The `PlayerAgent` can move and draw itself, and it interacts with the `TreasureAgent`.
A. COOL Program: `agent_adventure.cool`
-- This is a COOL program demonstrating Agents running in parallel!
-- We will have a player agent and a treasure agent.
-- The player can move, and if it reaches the treasure, it wins!
START
CANVAS SIZE 700 500 -- Set up a drawing area
-- Define the PlayerAgent Type
MAKE AGENT TYPE PlayerAgent
REMEMBER my_x IS -200
REMEMBER my_y IS 0
REMEMBER my_size IS 40
REMEMBER my_color IS "blue"
REMEMBER my_score IS 0
REMEMBER collision_status IS FALSE -- To store collision result
TO DRAW DO
COLOR MY my_color
DRAW SQUARE AT MY my_x MY my_y SIZE MY my_size
END FUNCTION
TO MoveRight BY step_size DO
REMEMBER MY my_x IS MY my_x PLUS step_size
SAY "Player moved to X: " AND MY my_x
END FUNCTION
TO MoveLeft BY step_size DO
REMEMBER MY my_x IS MY my_x MINUS step_size
SAY "Player moved to X: " AND MY my_x
END FUNCTION
TO CheckCollision WITH other_x AND other_y AND other_size DO
-- Simple collision check: if player's center is within other's bounds
REMEMBER collision_detected IS FALSE
IF MY my_x IS GREATER THAN (other_x MINUS other_size / 2) THEN
IF MY my_x IS LESS THAN (other_x PLUS other_size / 2) THEN
IF MY my_y IS GREATER THAN (other_y MINUS other_size / 2) THEN
IF MY my_y IS LESS THAN (other_y PLUS other_size / 2) THEN
REMEMBER collision_detected IS TRUE
END IF
END IF
END IF
END IF
SAY "Collision check result: " AND collision_detected
REMEMBER MY collision_status IS collision_detected -- Store result in player's property
END FUNCTION
TO IncreaseScore BY points DO
REMEMBER MY my_score IS MY my_score PLUS points
SAY "Score increased! New score: " AND MY my_score
END FUNCTION
TO UPDATE DO
-- Player doesn't move on its own, its movement is based on user input.
-- But it still gets to update its internal state (if any) and then draws itself.
-- The DRAW call is automatically added by the transpiler after this block.
END FUNCTION
END AGENT TYPE
-- Define the TreasureAgent Type
MAKE AGENT TYPE TreasureAgent
REMEMBER my_x IS 250
REMEMBER my_y IS 0
REMEMBER my_size IS 30
REMEMBER my_color IS "gold"
REMEMBER is_collected IS FALSE
TO DRAW DO
IF MY is_collected IS FALSE THEN
COLOR MY my_color
DRAW CIRCLE AT MY my_x MY my_y SIZE MY my_size
END IF
END FUNCTION
TO Collect DO
REMEMBER MY is_collected IS TRUE
SAY "Treasure collected!"
END FUNCTION
TO UPDATE DO
-- Treasure doesn't move or change on its own, it just waits to be collected.
-- The DRAW call is automatically added by the transpiler after this block.
END FUNCTION
END AGENT TYPE
-- Create our specific Agents
CREATE player1 AS PlayerAgent
CREATE shiny_treasure AS TreasureAgent
SAY "Move the blue square (player) to collect the gold circle (treasure)!"
SAY "Type 'right' to move right, 'left' to move left, or 'quit' to stop."
REMEMBER game_over IS FALSE
LOOP FOREVER DO -- Game loop runs continuously
IF game_over IS TRUE THEN
SAY "Game is over! Thanks for playing!"
BREAK LOOP -- Exit the loop
END IF
-- Step 1: Process any messages sent to agents (like 'MoveRight')
PROCESS ALL AGENT MESSAGES
-- Step 2: Handle user input for player movement
REMEMBER player_input IS ""
LISTEN FOR player_input AS TEXT
IF player_input IS EQUAL TO "right" THEN
TELL player1 TO MoveRight BY 30
ELSE IF player_input IS EQUAL TO "left" THEN
TELL player1 TO MoveLeft BY 30
ELSE IF player_input IS EQUAL TO "quit" THEN
SAY "Quitting game..."
REMEMBER game_over IS TRUE
ELSE
SAY "Invalid move. Type 'right', 'left', or 'quit'."
END IF
-- Step 3: Clear the screen for the new frame
CLEAR SCREEN
-- Step 4: Tell all agents to update themselves (which includes drawing)
UPDATE ALL AGENTS
-- Step 5: Check for collision between player and treasure
TELL player1 TO CheckCollision WITH MY shiny_treasure my_x AND MY shiny_treasure my_y AND MY shiny_treasure my_size
IF MY player1 collision_status IS TRUE THEN
IF MY shiny_treasure is_collected IS FALSE THEN -- Only collect if not already collected
TELL shiny_treasure TO Collect
TELL player1 TO IncreaseScore BY 100
SAY "You found the treasure! You win!"
REMEMBER game_over IS TRUE
END IF
END IF
-- Step 6: A small delay to control game speed
WAIT 0.1 SECONDS
END LOOP
SAY "Game ended. Your final score: " AND MY player1 my_score AND "."
-- Ask the smart brain for a fun fact about adventure games
SAY "Let's ask the Smart Brain for a fun fact about adventure games!"
REMEMBER brain_fact IS ""
ASK BRAIN "Tell me a fun fact about classic adventure games." AND GET ANSWER INTO brain_fact
SAY "Smart Brain says: " AND brain_fact
FINISH
B. Transpiled Python Code: `agent_adventure.py`
# This Python code was generated by the COOL Transpiler from agent_adventure.cool
# It requires cool_graphics.py, cool_llm_interface.py, and cool_agents.py to run.
import cool_graphics
import cool_llm_interface
import cool_agents
import time # For WAIT command
def run_cool_program():
cool_graphics.canvas_size(700, 500) # Set up a drawing area
# Define the PlayerAgent Type
class PlayerAgent(cool_agents.BaseAgent):
def __init__(self):
super().__init__()
self.my_x = -200
self.my_y = 0
self.my_size = 40
self.my_color = "blue"
self.my_score = 0
self.collision_status = False # To store collision result
def draw(self):
cool_graphics.set_color(self.my_color)
cool_graphics.draw_square(self.my_x, self.my_y, self.my_size)
def move_right(self, step_size):
self.my_x = self.my_x + step_size
print("Player moved to X: " + str(self.my_x))
def move_left(self, step_size):
self.my_x = self.my_x - step_size
print("Player moved to X: " + str(self.my_x))
def check_collision(self, other_x, other_y, other_size):
collision_detected = False
if self.my_x > (other_x - other_size / 2):
if self.my_x < (other_x + other_size / 2):
if self.my_y > (other_y - other_size / 2):
if self.my_y < (other_y + other_size / 2):
collision_detected = True
print("Collision check result: " + str(collision_detected))
self.collision_status = collision_detected # Store result in player's property
def increase_score(self, points):
self.my_score = self.my_score + points
print("Score increased! New score: " + str(self.my_score))
def update(self):
# Player doesn't move on its own, its movement is based on user input.
# But it still gets to update its internal state (if any) and then draws itself.
# The DRAW call is automatically added by the transpiler after this block.
self._post_update_draw() # Transpiler adds this line
# Define the TreasureAgent Type
class TreasureAgent(cool_agents.BaseAgent):
def __init__(self):
super().__init__()
self.my_x = 250
self.my_y = 0
self.my_size = 30
self.my_color = "gold"
self.is_collected = False
def draw(self):
if self.is_collected == False:
cool_graphics.set_color(self.my_color)
cool_graphics.draw_circle(self.my_x, self.my_y, self.my_size)
def collect(self):
self.is_collected = True
print("Treasure collected!")
def update(self):
# Treasure doesn't move or change on its own, it just waits to be collected.
# The DRAW call is automatically added by the transpiler after this block.
self._post_update_draw() # Transpiler adds this line
# Create our specific Agents
player1 = PlayerAgent()
shiny_treasure = TreasureAgent()
print("Move the blue square (player) to collect the gold circle (treasure)!")
print("Type 'right' to move right, 'left' to move left, or 'quit' to stop.")
game_over = False
while True: # Game loop runs continuously
if game_over == True:
print("Game is over! Thanks for playing!")
break # Exit the loop
# Step 1: Process any messages sent to agents (like 'MoveRight')
cool_agents.process_all_agent_messages()
# Step 2: Handle user input for player movement
player_input = ""
player_input = input("player_input: ")
if player_input == "right":
player1._send_message("move_right", 30)
elif player_input == "left":
player1._send_message("move_left", 30)
elif player_input == "quit":
print("Quitting game...")
game_over = True
else:
print("Invalid move. Type 'right', 'left', or 'quit'.")
# Step 3: Clear the screen for the new frame
cool_graphics.clear_screen()
# Step 4: Tell all agents to update themselves (which includes drawing)
cool_agents.update_all_agents()
cool_graphics.update_screen() # Single screen update after all drawing
# Step 5: Check for collision between player and treasure
player1._send_message("check_collision", shiny_treasure.my_x, shiny_treasure.my_y, shiny_treasure.my_size)
cool_agents.process_all_agent_messages() # Process collision check message immediately
if player1.collision_status == True:
if shiny_treasure.is_collected == False: # Only collect if not already collected
shiny_treasure._send_message("collect")
player1._send_message("increase_score", 100)
cool_agents.process_all_agent_messages() # Process collect and score messages immediately
print("You found the treasure! You win!")
game_over = True
# Step 6: A small delay to control game speed
time.sleep(0.1)
print("Game ended. Your final score: " + str(player1.my_score) + ".")
# Ask the smart brain for a fun fact about adventure games
print("Let's ask the Smart Brain for a fun fact about adventure games!")
brain_fact = ""
brain_fact = cool_llm_interface.ask_llm("Tell me a fun fact about classic adventure games.")
print("Smart Brain says: " + str(brain_fact))
cool_agents.stop_all_agents() # Clean up agents
cool_graphics.keep_window_open()
if __name__ == '__main__':
run_cool_program()
C. Python Helper Module: `cool_graphics.py`
# cool_graphics.py
# This module provides simple graphics functions for COOL programs,
# using Python's built-in 'turtle' graphics library.
import turtle
_screen = None
_turtle = None
_canvas_width = 0
_canvas_height = 0
def _init_graphics():
"""Initializes the turtle graphics screen and turtle object."""
global _screen, _turtle
if _screen is None:
_screen = turtle.Screen()
_screen.tracer(0) # Turn off screen updates for smoother drawing
_screen.title("COOL Graphics Window") # Give the window a title
_screen.bgcolor("white") # Default background color
if _turtle is None:
_turtle = turtle.Turtle()
_turtle.speed(0) # Fastest speed for drawing
_turtle.hideturtle() # Hide the turtle icon
_turtle.penup() # Start with pen up by default
def canvas_size(width, height):
"""
Sets the size of the drawing canvas, with (0,0) at the center.
All drawing coordinates will be relative to this center.
"""
global _canvas_width, _canvas_height
_init_graphics()
_canvas_width = width
_canvas_height = height
_screen.setup(width=width, height=height)
# Set world coordinates to have (0,0) at the center
_screen.setworldcoordinates(-width/2, -height/2, width/2, height/2)
# _screen.update() # Initial update will be handled by main loop's update_screen
def set_color(color_name):
"""Sets the current drawing color for subsequent shapes or lines."""
_init_graphics()
try:
_turtle.pencolor(color_name.lower())
_turtle.fillcolor(color_name.lower())
except turtle.TurtleGraphicsError:
print(f"Warning: Unknown color '{color_name}'. Using black.")
_turtle.pencolor("black")
_turtle.fillcolor("black")
def draw_square(x, y, size):
"""Draws a filled square centered at (x, y) with a given side size."""
_init_graphics()
_turtle.penup()
# Turtle draws from its current position. To center a square,
# move to its bottom-left corner relative to the center (x,y).
_turtle.goto(x - size / 2, y - size / 2)
_turtle.pendown()
_turtle.begin_fill()
for _ in range(4):
_turtle.forward(size)
_turtle.right(90) # Turn right for square
_turtle.end_fill()
def draw_circle(x, y, radius):
"""Draws a filled circle centered at (x, y) with a given radius."""
_init_graphics()
_turtle.penup()
# Turtle draws a circle with its center at (x, y - radius)
_turtle.goto(x, y - radius)
_turtle.pendown()
_turtle.begin_fill()
_turtle.circle(radius)
_turtle.end_fill()
def move_to(x, y):
"""Moves the turtle to a specific (x, y) coordinate without drawing."""
_init_graphics()
_turtle.penup()
_turtle.goto(x, y)
def pen_down():
"""Puts the pen down to start drawing when moving."""
_init_graphics()
_turtle.pendown()
def pen_up():
"""Lifts the pen up to stop drawing when moving."""
_init_graphics()
_turtle.penup()
def forward(distance):
"""Moves the turtle forward by a given distance, drawing if pen is down."""
_init_graphics()
_turtle.forward(distance)
def turn(angle):
"""Turns the turtle right by a given angle in degrees."""
_init_graphics()
_turtle.right(angle)
def clear_screen():
"""Clears all drawings from the screen and redraws a white background."""
_init_graphics()
_turtle.clear() # Clears turtle's drawings
_turtle.penup()
_turtle.home() # Reset turtle to center (0,0) and heading 0
_turtle.goto(-_canvas_width/2, -_canvas_height/2) # Go to bottom-left of canvas
_turtle.fillcolor("white")
_turtle.begin_fill()
for _ in range(4):
_turtle.forward(_canvas_width if _ % 2 == 0 else _canvas_height)
_turtle.right(90)
_turtle.end_fill()
_turtle.penup()
_turtle.home() # Return turtle to center (0,0) and heading 0, pen up
def update_screen():
"""Performs a single screen update after all drawing operations."""
_init_graphics()
_screen.update()
def keep_window_open():
"""Keeps the graphics window open until manually closed by the user."""
_init_graphics()
if _screen:
_screen.exitonclick()
else:
print("Graphics window was not created. Program finished.")
D. Python Helper Module: `cool_llm_interface.py`
# cool_llm_interface.py
# This module provides a simplified interface for Large Language Model (LLM)
# interaction for COOL programs.
# In a real-world scenario, this would connect to an actual LLM API (e.g., OpenAI, Google Gemini).
# For this example, it provides a mock response to simulate LLM behavior.
def ask_llm(question):
"""
Sends a question to a conceptual LLM and returns a simplified answer.
In a real application, this would involve API calls and error handling.
For demonstration, it provides a predefined or simple generated response.
"""
print(f"--- Smart Brain thinking about: '{question}' ---") # For debugging purposes
# Simple predefined responses for common questions
if "abstract art" in question.lower():
return "Abstract art is like drawing or painting feelings and ideas " \
"instead of real things you can see. Artists use colors, " \
"shapes, and lines to show what they imagine or feel, " \
"making art that looks different from a photograph!"
elif "happy animal" in question.lower():
return "A fluffy puppy jumping in a field of flowers!"
elif "fun shape" in question.lower():
return "A star with many points, shining brightly!"
elif "color" in question.lower():
return "Rainbow colors, all mixed together!"
elif "what is a cat" in question.lower():
return "A cat is a small, furry animal often kept as a pet. " \
"They are known for being playful, graceful, and sometimes a bit mischievous!"
elif "fun fact about space" in question.lower():
return "Did you know that there are more stars in the universe " \
"than grains of sand on all the beaches on Earth? That's a lot of stars!"
elif "classic adventure games" in question.lower():
return "Did you know that some of the very first adventure games " \
"were entirely text-based? You would type commands like 'GO NORTH' " \
"or 'PICK UP SWORD' and the computer would describe what happened next!"
else:
# A generic response for other questions
return "That's a very interesting question! The smart brain is still " \
"learning about that, but it thinks it's quite fascinating."
E. Python Helper Module: `cool_agents.py`
# cool_agents.py
# This module provides the core logic for COOL Agents, managing their messages
# and updates to give the illusion of parallel execution.
import queue
# A global list to keep track of all active agents for the main loop to manage
_all_active_agents = []
class BaseAgent:
"""
Base class for all COOL Agents. Manages message queuing and the update/draw cycle.
"""
def __init__(self):
self._message_queue = queue.Queue()
self._is_active = True # For internal management
_all_active_agents.append(self) # Add self to the global list
def _send_message(self, func_name, *args):
"""Queues a message (function call) for this agent to process later."""
self._message_queue.put((func_name, args))
def _process_messages(self):
"""Processes all messages currently in this agent's queue."""
while not self._message_queue.empty():
try:
func_name, args = self._message_queue.get_nowait()
method = getattr(self, func_name)
method(*args)
self._message_queue.task_done()
except queue.Empty:
# Should not happen with get_nowait and !empty check, but good practice
pass
except AttributeError:
print(f"Agent error: Method '{func_name}' not found for agent {self.__class__.__name__}.")
except Exception as e:
print(f"Agent {self.__class__.__name__} error processing message '{func_name}': {e}")
def _post_update_draw(self):
"""
Called automatically by the transpiler after the agent's update logic.
It triggers the agent's draw method if one exists.
"""
if hasattr(self, 'draw') and callable(getattr(self, 'draw')):
self.draw()
def stop(self):
"""Marks the agent as inactive and removes it from global tracking."""
self._is_active = False
# Remove from global list to prevent further processing
if self in _all_active_agents:
_all_active_agents.remove(self)
# Global functions for the main program loop to interact with agents
def process_all_agent_messages():
"""Tells all active agents to process any messages in their queues."""
for agent in list(_all_active_agents): # Iterate over a copy to allow removal during iteration
if agent._is_active:
agent._process_messages()
def update_all_agents():
"""Tells all active agents to run their 'update' logic and then draw themselves."""
for agent in list(_all_active_agents):
if agent._is_active:
if hasattr(agent, 'update') and callable(getattr(agent, 'update')):
agent.update() # The update method itself will call _post_update_draw()
def stop_all_agents():
"""Stops all active agents and clears the global agent list."""
for agent in list(_all_active_agents):
agent.stop()
_all_active_agents.clear()
F. The COOL Transpiler: `cool_transpiler.py`
# cool_transpiler.py
# This program translates COOL code into Python code and then runs it.
import re
import sys
import os
import subprocess # Needed to run the generated Python file
# Define COOL keywords and their Python equivalents or special handling.
# This dictionary helps the transpiler know how to translate words.
KEYWORDS = {
"START": "", # 'START' just marks the beginning, no Python code needed
"FINISH": "cool_graphics.keep_window_open()", # Calls a function to keep graphics window open
"REMEMBER": "", # Handled specially for variable assignment (global or agent property)
"IS": "=", # 'IS' becomes '=' for assignment or '==' for comparison
"TRUE": "True", # COOL's TRUE is Python's True
"FALSE": "False", # COOL's FALSE is Python's False
"PLUS": "+", # Math operators
"MINUS": "-",
"TIMES": "*",
"DIVIDED BY": "/",
"AND": "+", # For combining text in SAY statements (handled by print with str conversion)
"IF": "if",
"THEN": ":", # 'THEN' starts a new block in Python
"ELSE IF": "elif", # For multiple conditions
"ELSE": "else:",
"END IF": "", # 'END IF' just marks the end of a block, no Python code
"LOOP": "", # Handled specially for 'TIMES DO' or 'FOREVER DO'
"TIMES": "", # Part of 'LOOP N TIMES DO'
"FOREVER": "", # Part of 'LOOP FOREVER DO'
"DO": ":", # 'DO' starts a new block in Python
"END LOOP": "", # 'END LOOP' just marks the end of a block
"BREAK LOOP": "break", # New command to exit loops
"SAY": "print", # Direct to Python's print function
"LISTEN FOR": "", # Handled specially for input
"AS TEXT": "", # Part of 'LISTEN FOR ... AS TEXT'
"AS NUMBER": "", # Part of 'LISTEN FOR ... AS NUMBER'
"AS TRUTH": "", # Part of 'LISTEN FOR ... AS TRUTH'
"CANVAS SIZE": "cool_graphics.canvas_size", # Graphics functions
"COLOR": "cool_graphics.set_color", # Standalone color setting
"DRAW SQUARE": "cool_graphics.draw_square", # Drawing specific shapes
"DRAW CIRCLE": "cool_graphics.draw_circle",
"MOVE TO": "cool_graphics.move_to", # Turtle-like movement
"PEN DOWN": "cool_graphics.pen_down()",
"PEN UP": "cool_graphics.pen_up()",
"FORWARD": "cool_graphics.forward",
"TURN": "cool_graphics.turn",
"CLEAR SCREEN": "cool_graphics.clear_screen()", # New command to clear graphics
"WAIT": "", # Handled specially for time delay
"SECONDS": "", # Part of 'WAIT N SECONDS'
"ASK BRAIN": "cool_llm_interface.ask_llm", # Calls the LLM interface
"AND GET ANSWER INTO": "", # Part of 'ASK BRAIN ... AND GET ANSWER INTO ...'
"IS EQUAL TO": "==", # Comparison operators
"IS GREATER THAN": ">",
"IS LESS THAN": "<",
"MAKE AGENT TYPE": "", # Handled specially for class definition
"END AGENT TYPE": "", # End of class definition
"TO": "", # Start of a method definition
"END FUNCTION": "", # End of a method definition
"MY": "self.", # For accessing agent properties
"MYSELF": "self", # For an agent to refer to itself in TELL commands
"CREATE": "", # Handled specially for agent instantiation
"AS": "", # Part of 'CREATE ... AS ...'
"TELL": "", # Handled specially for message calls to agents
"WITH": "", # Part of 'TELL ... WITH ...'
"BY": "", # Part of 'TELL ... BY ...' (for functions with single argument)
"PROCESS ALL AGENT MESSAGES": "cool_agents.process_all_agent_messages()", # New agent management command
"UPDATE ALL AGENTS": "cool_agents.update_all_agents()", # New agent management command
}
def _transpile_expression(expr_str):
"""
Converts COOL expressions (like math or combining text) into Python.
Example: "5 PLUS 3" becomes "5 + 3"
Example: "my_name AND "!" " becomes "str(my_name) + '!'"
"""
# Replace COOL math operators with Python equivalents
expr_str = re.sub(r'\bPLUS\b', '+', expr_str, flags=re.IGNORECASE)
expr_str = re.sub(r'\bMINUS\b', '-', expr_str, flags=re.IGNORECASE)
expr_str = re.sub(r'\bTIMES\b', '*', expr_str, flags=re.IGNORECASE)
expr_str = re.sub(r'\bDIVIDED BY\b', '/', expr_str, flags=re.IGNORECASE)
# Handle 'AND' for string concatenation. We need to ensure variables are converted to str.
parts = re.split(r'\bAND\b', expr_str, flags=re.IGNORECASE)
processed_parts = []
for p in parts:
p_stripped = p.strip()
# If it's a string literal (starts and ends with quotes)
if p_stripped.startswith('"') and p_stripped.endswith('"'):
processed_parts.append(p_stripped)
# If it looks like a variable name (not MY property)
elif re.match(r'^[a-zA-Z_][a-zA-Z0-9_]*$', p_stripped) and not p_stripped.upper().startswith("MY "):
processed_parts.append(f"str({p_stripped})") # Convert variable to string
else: # It might be a MY property, number, or a direct value
processed_parts.append(p_stripped)
expr_str = " + ".join(processed_parts)
# Replace COOL boolean literals with Python equivalents
expr_str = re.sub(r'\bTRUE\b', 'True', expr_str, flags=re.IGNORECASE)
expr_str = re.sub(r'\bFALSE\b', 'False', expr_str, flags=re.IGNORECASE)
# Handle 'MY' for agent properties
expr_str = re.sub(r'\bMY\s+([a-zA-Z_][a-zA-Z0-9_]*)\b', r'self.\1', expr_str, flags=re.IGNORECASE)
return expr_str
def _transpile_condition(condition_str):
"""
Converts COOL comparison conditions into Python.
Example: "my_age IS GREATER THAN 10" becomes "my_age > 10"
"""
condition_str = re.sub(r'\bIS EQUAL TO\b', '==', condition_str, flags=re.IGNORECASE)
condition_str = re.sub(r'\bIS GREATER THAN\b', '>', condition_str, flags=re.IGNORECASE)
condition_str = re.sub(r'\bIS LESS THAN\b', '<', condition_str, flags=re.IGNORECASE)
# Also transpile boolean literals within conditions
condition_str = re.sub(r'\bTRUE\b', 'True', condition_str, flags=re.IGNORECASE)
condition_str = re.sub(r'\bFALSE\b', 'False', condition_str, flags=re.IGNORECASE)
# Handle 'MY' for agent properties within conditions
condition_str = re.sub(r'\bMY\s+([a-zA-Z_][a-zA-Z0-9_]*)\b', r'self.\1', condition_str, flags=re.IGNORECASE)
return condition_str
def parse_and_transpile(cool_code_lines):
"""
Takes a list of COOL code lines and translates them into Python code.
It manages indentation for Python blocks (like if statements, loops, classes, and methods).
"""
python_code_lines = [
"import cool_graphics",
"import cool_llm_interface",
"import cool_agents", # New import for agent management
"import time", # For WAIT command
"",
"def run_cool_program():" # Wrap the entire COOL program in a Python function
]
indent_level = 1 # All code inside run_cool_program() starts with one level of indentation
in_agent_type = False # State flag: Are we currently inside an AGENT TYPE definition?
current_function_is_update = False # State flag: Is the current function being defined 'UPDATE'?
for line_num, cool_line in enumerate(cool_code_lines):
original_line = cool_line.strip() # Remove leading/trailing spaces
if not original_line: # Skip empty lines
continue
# Handle comments first
if original_line.startswith("--"):
comment_content = original_line[2:].strip()
python_code_lines.append(" " * indent_level + "# " + comment_content)
continue
py_line = "" # This will hold the translated Python code for the current line
current_indent = " " * indent_level # Calculate current indentation
upper_line = original_line.upper() # Convert to uppercase for case-insensitive matching
# --- Agent Type and Function Definitions ---
if upper_line.startswith("MAKE AGENT TYPE"):
agent_type_name = original_line[len("MAKE AGENT TYPE"):].strip()
python_code_lines.append(f"{current_indent}class {agent_type_name}(cool_agents.BaseAgent):") # Inherit from BaseAgent
python_code_lines.append(f"{current_indent} def __init__(self):")
python_code_lines.append(f"{current_indent} super().__init__()") # Call parent constructor
indent_level += 2 # For __init__ method
in_agent_type = True
continue
elif upper_line.startswith("END AGENT TYPE"):
indent_level -= 2 # Exit __init__ and class definition
in_agent_type = False
continue
elif upper_line.startswith("TO "):
func_signature = original_line[len("TO"):].strip()
method_name_py = func_signature.lower().replace(' ', '_')
# Extract actual method name part (before WITH/BY)
method_name_cool_raw = func_signature
if " WITH " in func_signature.upper():
method_name_cool_raw = func_signature.upper().split(" WITH ", 1)[0]
elif " BY " in func_signature.upper():
method_name_cool_raw = func_signature.upper().split(" BY ", 1)[0]
method_name_py = method_name_cool_raw.strip().lower().replace(' ', '_')
if " WITH " in func_signature.upper():
func_name_part, args_part = func_signature.split(" WITH ", 1)
args = [arg.strip() for arg in args_part.split(" AND ")]
py_args = ", ".join(args)
py_line = f"def {method_name_py}(self, {py_args}):"
elif " BY " in func_signature.upper(): # Special case for single argument like "MoveRight BY distance"
func_name_part, arg_name = func_signature.split(" BY ", 1)
py_line = f"def {method_name_py}(self, {arg_name.strip()}):"
else:
py_line = f"def {method_name_py}(self):"
python_code_lines.append(current_indent + py_line)
indent_level += 1 # For method body
current_function_is_update = (method_name_py == "update") # Set flag if it's the update method
continue
elif upper_line.startswith("END FUNCTION"):
if current_function_is_update:
python_code_lines.append(current_indent + " self._post_update_draw()") # Auto-add draw call for UPDATE
current_function_is_update = False # Reset flag
indent_level -= 1 # Exit method body
continue
# --- Handle Control Flow (START/FINISH, IF/ELSE, LOOP) ---
elif upper_line.startswith("START"):
continue # Handled by function definition
elif upper_line.startswith("FINISH"):
python_code_lines.append(current_indent + "cool_agents.stop_all_agents()") # Clean up agents
py_line = KEYWORDS["FINISH"] # Calls cool_graphics.keep_window_open()
elif upper_line.startswith("IF"):
condition_str = original_line[len("IF"):].split("THEN")[0].strip()
py_condition = _transpile_condition(condition_str)
py_line = f"if {py_condition}:"
indent_level += 1 # Increase indent for the block inside IF
elif upper_line.startswith("ELSE IF"):
indent_level -= 1 # End previous block
condition_str = original_line[len("ELSE IF"):].split("THEN")[0].strip()
py_condition = _transpile_condition(condition_str)
py_line = f"elif {py_condition}:"
indent_level += 1 # Start new block
elif upper_line.startswith("ELSE"):
indent_level -= 1 # End previous block
py_line = "else:"
indent_level += 1 # Start new block
elif upper_line.startswith("END IF"):
indent_level -= 1 # Decrease indent after END IF
continue # No Python line for END IF itself
elif upper_line.startswith("LOOP"):
if "TIMES DO" in upper_line:
parts = upper_line.split("TIMES DO")
num_times = original_line[len("LOOP"):len(parts[0])].strip()
py_line = f"for _ in range({num_times}):"
indent_level += 1 # Increase indent for the loop block
elif "FOREVER DO" in upper_line: # New LOOP FOREVER
py_line = "while True:"
indent_level += 1
else:
raise ValueError(f"Invalid LOOP statement at line {line_num+1}: {original_line}")
elif upper_line.startswith("BREAK LOOP"):
py_line = "break"
elif upper_line.startswith("END LOOP"):
indent_level -= 1 # Decrease indent after END LOOP
continue # No Python line for END LOOP itself
# --- Handle Variable Assignment (REMEMBER) ---
elif upper_line.startswith("REMEMBER"):
parts = original_line[len("REMEMBER"):].split("IS", 1)
var_name_part = parts[0].strip()
value_expr = parts[1].strip()
py_value = _transpile_expression(value_expr)
if var_name_part.upper().startswith("MY "):
# Agent property assignment
prop_name = var_name_part[len("MY "):].strip()
py_line = f"self.{prop_name} = {py_value}"
else:
# Global variable assignment
py_line = f"{var_name_part} = {py_value}"
# --- Handle Agent Creation and Message Calls ---
elif upper_line.startswith("CREATE"):
# Example: CREATE player1 AS PlayerAgent
parts = original_line[len("CREATE"):].split("AS", 1)
agent_instance_name = parts[0].strip()
agent_type = parts[1].strip()
py_line = f"{agent_instance_name} = {agent_type}()"
elif upper_line.startswith("TELL"):
# Example: TELL player1 TO MoveRight BY 30
# Example: TELL player1 TO CheckCollision WITH other_x AND other_y AND other_size
# Example: TELL MYSELF TO Move
tell_parts = original_line[len("TELL"):].strip().split("TO", 1)
agent_instance_cool = tell_parts[0].strip()
agent_instance_py = KEYWORDS.get(agent_instance_cool.upper(), agent_instance_cool) # Handle MYSELF
method_call_part = tell_parts[1].strip()
# Extract method name part first (before WITH/BY)
method_name_cool_raw = method_call_part
args_part_raw = None
if " WITH " in method_call_part.upper():
method_name_cool_raw, args_part_raw = method_call_part.split(" WITH ", 1)
elif " BY " in method_call_part.upper():
method_name_cool_raw, args_part_raw = method_call_part.split(" BY ", 1)
method_name_py = method_name_cool_raw.strip().lower().replace(' ', '_')
if args_part_raw: # If there were arguments (WITH or BY)
if " WITH " in method_call_part.upper(): # Original was WITH
args = [arg.strip() for arg in args_part_raw.split(" AND ")]
py_args = ", ".join([_transpile_expression(arg) for arg in args])
py_line = f"{agent_instance_py}._send_message(\"{method_name_py}\", {py_args})"
else: # Original was BY
py_arg = _transpile_expression(args_part_raw.strip())
py_line = f"{agent_instance_py}._send_message(\"{method_name_py}\", {py_arg})"
else: # No arguments
py_line = f"{agent_instance_py}._send_message(\"{method_name_py}\")"
# --- Handle Other Commands ---
elif upper_line.startswith("SAY"):
content_expr = original_line[len("SAY"):].strip()
py_content = _transpile_expression(content_expr)
py_line = f"print({py_content})"
elif upper_line.startswith("LISTEN FOR"):
parts = original_line[len("LISTEN FOR"):].split("AS", 1)
var_name = parts[0].strip()
type_spec = parts[1].strip().upper() if len(parts) > 1 else "TEXT" # Default to TEXT
prompt = f'"{var_name.replace("_", " ").title()}: "'
if type_spec == "TEXT":
py_line = f"{var_name} = input({prompt})"
elif type_spec == "NUMBER":
py_line = f"{var_name} = int(input({prompt}))"
elif type_spec == "TRUTH":
py_line = f"{var_name} = input({prompt} + '(TRUE/FALSE)').upper() == 'TRUE'"
else:
raise ValueError(f"Unknown type for LISTEN FOR at line {line_num+1}: {type_spec}")
elif upper_line.startswith("CANVAS SIZE"):
parts = original_line.upper().split("SIZE")
size_values = parts[1].strip().split()
width = size_values[0]
height = size_values[1]
py_line = f"cool_graphics.canvas_size({width}, {height})"
elif upper_line.startswith("COLOR"):
color_name = original_line[len("COLOR"):].strip()
py_line = f"cool_graphics.set_color({color_name.upper()!r})"
elif upper_line.startswith("DRAW SQUARE"):
parts_after_shape = original_line[len("DRAW SQUARE"):].strip()
at_match = re.search(r'AT (-?\d+) (-?\d+)', parts_after_shape, flags=re.IGNORECASE)
size_match = re.search(r'SIZE (\d+)', parts_after_shape, flags=re.IGNORECASE)
x = at_match.group(1) if at_match else "0"
y = at_match.group(2) if at_match else "0"
size = size_match.group(1) if size_match else "50"
py_line = f"cool_graphics.draw_square({x}, {y}, {size})"
elif upper_line.startswith("DRAW CIRCLE"):
parts_after_shape = original_line[len("DRAW CIRCLE"):].strip()
at_match = re.search(r'AT (-?\d+) (-?\d+)', parts_after_shape, flags=re.IGNORECASE)
size_match = re.search(r'SIZE (\d+)', parts_after_shape, flags=re.IGNORECASE)
x = at_match.group(1) if at_match else "0"
y = at_match.group(2) if at_match else "0"
size = size_match.group(1) if size_match else "50"
py_line = f"cool_graphics.draw_circle({x}, {y}, {size})"
elif upper_line.startswith("MOVE TO"):
parts = original_line[len("MOVE TO"):].strip().split()
x = parts[0]
y = parts[1]
py_line = f"cool_graphics.move_to({x}, {y})"
elif upper_line.startswith("PEN DOWN"):
py_line = KEYWORDS["PEN DOWN"]
elif upper_line.startswith("PEN UP"):
py_line = KEYWORDS["PEN UP"]
elif upper_line.startswith("FORWARD"):
distance = original_line[len("FORWARD"):].strip()
py_line = f"cool_graphics.forward({distance})"
elif upper_line.startswith("TURN"):
angle = original_line[len("TURN"):].strip()
py_line = f"cool_graphics.turn({angle})"
elif upper_line.startswith("CLEAR SCREEN"): # New graphics command
py_line = KEYWORDS["CLEAR SCREEN"]
elif upper_line.startswith("WAIT"): # New WAIT command
parts = original_line[len("WAIT"):].strip().split("SECONDS")
delay_seconds = parts[0].strip()
py_line = f"time.sleep({delay_seconds})"
elif upper_line.startswith("ASK BRAIN"):
match = re.search(r'ASK BRAIN (.*?)\s+AND GET ANSWER INTO\s+([a-zA-Z_][a-zA-Z0-9_]*)', original_line, re.IGNORECASE)
if not match:
raise ValueError(f"Invalid ASK BRAIN format at line {line_num+1}: {original_line}")
prompt_text = match.group(1).strip()
var_name = match.group(2).strip()
py_line = f"{var_name} = cool_llm_interface.ask_llm({prompt_text})"
elif upper_line.startswith("PROCESS ALL AGENT MESSAGES"): # New agent management command
py_line = KEYWORDS["PROCESS ALL AGENT MESSAGES"]
elif upper_line.startswith("UPDATE ALL AGENTS"): # New agent management command
py_line = KEYWORDS["UPDATE ALL AGENTS"]
# After updating all agents, we need to update the screen once
python_code_lines.append(current_indent + "cool_graphics.update_screen()")
else:
raise ValueError(f"Unknown COOL command or syntax error at line {line_num+1}: '{original_line}'")
if py_line: # Only add lines that actually produced Python code
python_code_lines.append(current_indent + py_line)
# Add the main execution block for the generated Python file
python_code_lines.append("")
python_code_lines.append("if __name__ == '__main__':")
python_code_lines.append(" run_cool_program()")
return "\n".join(python_code_lines)
def main(cool_file_path):
"""
Main function to transpile and run a COOL program.
"""
try:
# Read the COOL code from the specified file
with open(cool_file_path, 'r') as f:
cool_code_lines = f.readlines()
# Translate the COOL code into Python
python_code = parse_and_transpile(cool_code_lines)
# Create a name for the output Python file
output_py_file = cool_file_path.replace(".cool", ".py")
if not output_py_file.endswith(".py"): # Fallback if .cool is missing
output_py_file += ".py"
# Save the transpiled Python code to the new file
with open(output_py_file, 'w') as f:
f.write(python_code)
print(f"COOL code transpiled to Python: {output_py_file}")
print("Running the COOL program...")
# Execute the generated Python code using the system's Python interpreter
# This makes sure the cool_graphics.py, cool_llm_interface.py, and cool_agents.py are found.
subprocess.run([sys.executable, output_py_file])
except FileNotFoundError:
print(f"Error: COOL file not found at '{cool_file_path}'")
except ValueError as e:
print(f"Transpilation Error: {e}")
except Exception as e:
print(f"An unexpected error occurred: {e}")
if __name__ == "__main__":
# This part runs when you execute cool_transpiler.py from the command line.
if len(sys.argv) != 2:
print("Usage: python cool_transpiler.py <your_program.cool>")
print("Example: python cool_transpiler.py my_drawing.cool")
else:
main(sys.argv[1])
To run the "Agent Adventure" example:
- Save the COOL program as `agent_adventure.cool`.
- Save the Python graphics module as `cool_graphics.py`.
- Save the Python LLM interface module as `cool_llm_interface.py`.
- Save the Python Agent helper module as `cool_agents.py`.
- Save the COOL Transpiler as `cool_transpiler.py`
- Ensure all five files are in the same folder.
- Open a terminal or command prompt and navigate to that folder.
- Run the transpiler with your COOL program: python cool_transpiler.py agent_adventure.cool
This will open a graphics window showing your agents, and the program will interact with you in the terminal, allowing you to control the player and collect the treasure! Have fun with your smart, parallel Agents!
No comments:
Post a Comment