# Writing Goals (Python)

## Overview <a href="#writing-goals" id="writing-goals"></a>

A *Goal* in Tychos is a short piece of code in a *scenario* which can check a student's work. Goals are written by the scenario's author, usually an instructor. To the student, the goals appear as a sentence describing something the student should do. At a time defined within the goal, the goal is "checked" and if the goal is satisfied, it turns green. Otherwise it turns red. If the goal's state has not yet been "checked" it will display a question mark.

<figure><img src="/files/XHZIXWJyzXOF5FjjFJCO" alt="Goals panel showing PASS, NOT YET, and FAIL states. The failing goal displays a hint."><figcaption><p>Goals showing PASS (✓), NOT YET (?), and FAIL (✗) states. Active hints appear below a failing goal.</p></figcaption></figure>

<figure><img src="/files/H7oplLIdAPuwG9LhJiAh" alt="Goals panel showing PASS, ERROR, and FAIL states."><figcaption><p>An ERROR state (⚠️) appears when the goal code itself throws an error, with the error message shown to instructors.</p></figcaption></figure>

## Goal States

Each goal displays one of four states:

| Icon | State       | Meaning                                                                            |
| ---- | ----------- | ---------------------------------------------------------------------------------- |
| ❓    | **NOT YET** | The **When Condition** has not been triggered yet                                  |
| ✓    | **PASS**    | The condition was triggered and the **Success Condition** returned `True`          |
| ✗    | **FAIL**    | The condition was triggered and the **Success Condition** returned `False`         |
| ⚠️   | **ERROR**   | The goal code threw an error (shown to instructors; displayed as FAIL to students) |

When a goal is in the FAIL or ERROR state, any active [hints](#adding-hints) will be shown below the goal description.

## Writing a Goal

In order to write a goal, you need to be logged in as an Instructor. Only instructors see the pencil edit buttons at the right side of the goal. Goals can be dragged up or down to reorder them — this doesn't affect when they're checked, just the order they appear.

When you click the pencil button to edit a goal, it expands into several edit fields:

* **Description** — the text the student sees
* **When Condition** — Python code that specifies when the goal is checked
* **Success Condition** — Python code that returns `True` (pass) or `False` (fail)
* **Hints** — optional hints to show students when the goal fails (see [Adding Hints](#adding-hints))

The edit toolbar shows three buttons: a ✓ to save, an ✗ to cancel, and a 🗑 to delete.

<figure><img src="/files/Yr9MTyGgFPu1z1nqAks2" alt="Goal editor showing Description, When Condition, Success Condition, and Hints fields."><figcaption><p>The goal editor with all fields visible, including a hint with an optional Show When condition.</p></figcaption></figure>

Here is a simple Goal:

**Description:** Create a Circle object called `c1` with a position of (10, 10), a radius of 5, and a color of "green"

**When Condition:** `frame_count == 0`

**Success Condition:**

```python
exists('c1') and equals(c1.pos, vec(10, 10)) and c1.radius == 5 and c1.color == "green"
```

The **When Condition** fires when `frame_count` is 0 — right after the Setup Code runs, before the first Loop calculation. The **Success Condition** is then evaluated and its result determines whether the goal passes or fails.

The simplest success conditions are just a single expression:

```python
exists('c1') and equals(c1.pos, vec(10, 10))
```

For more complex checks you can use a conditional statement that must return True or False:

```python
if not exists('c1'):
  return False
elif not equals(c1.pos, vec(10, 10)):
  return False 
elif c1.radius != 5:
  return False
elif c1.color != "green":
  return False
else:
  return True
```

To satisfy this goal, the student writes:

```python
# Setup Code
c1 = circle(pos=vec(10, 10), radius=5, color="green")
```

{% hint style="info" %}
When comparing vectors for equality, always use `equals()` instead of `==`. For example: `equals(c1.pos, vec(10, 10))`.
{% endhint %}

## Testing *Future* Conditions

Here is a goal that checks whether a student moves the circle to a new position at a specific time:

**Description:** Move the circle so that it is at (20, 20) at time t = 2 seconds.

**When Condition:** `t == 2`

**Success Condition:**

```python
exists('c1') and equals(c1.pos, vec(20, 20))
```

The **When Condition** fires when `t` reaches 2, so the goal is checked at that moment in the simulation. You can use any expression that evaluates to `True` or `False`:

```python
# Check on a specific frame
frame_count == 10

# Check at a specific time
t == 2.0

# Check continuously once time exceeds a threshold
t > 2
```

When using `t > 2`, the goal will be re-evaluated every frame after 2 seconds — useful for checking conditions that must be sustained.

## Goal Helper Functions

Several helper functions are available in all goal code (**When Condition**, **Success Condition**, and **Hint Conditions**):

### `exists(name)`

Returns `True` if a variable with the given name has been defined in the simulation. This is the safest way to check for a student variable before accessing it.

```python
# Good — safe even if the student hasn't defined 'ball' yet
exists('ball') and ball.pos.x > 5

# Risky — throws a NameError if 'ball' doesn't exist
ball.pos.x > 5
```

### `close(a, b, tolerance=1e-9)`

Returns `True` if `a` and `b` are within `tolerance` of each other. Works with both numbers and vectors (uses Euclidean distance for vectors). Use this when checking computed values that may have floating-point rounding.

```python
# Check that a computed speed is close to 9.8 m/s
close(speed, 9.8, 0.1)

# Check that a vector is approximately at a position
close(ball.pos, vec(10, 20), 0.5)
```

### `between(value, min, max)`

Returns `True` if `min < value < max` (exclusive on both ends). Useful for range checks.

```python
# Check that an angle is in the right quadrant
between(angle, 0, 1.57)

# Check that the ball is within bounds
between(ball.pos.x, -50, 50)
```

### `has_attr(obj, name)`

Returns `True` if `obj` has an attribute with the given name.

```python
# Check that the student added a 'color' property
exists('ball') and has_attr(ball, 'color')
```

### `equals(a, b)`

Returns `True` if two vectors are equal. Required for vector comparisons — do not use `==` with vectors.

```python
equals(ball.pos, vec(0, 0))
equals(velocity, vec(3, 4))
```

## Checking Values at Specific Frames

The `frame(n)` function lets you read what a variable's value was at a specific simulation frame. This is useful for goals that need to verify something happened at a particular moment in the past.

```python
# Check the position at frame 0 (start of simulation)
frame(0).ball_pos

# Check the velocity at frame 10
frame(10).velocity
```

`frame(n)` returns a snapshot object — access any variable by name as a property. It returns `None` if the variable wasn't defined at that frame or the frame index is out of range.

{% hint style="info" %}
Tychos only snapshots variables that appear in a `frame(n).varname` expression in your goal code. You don't need to configure anything — Tychos scans your goal code at setup time and automatically tracks the referenced variables.
{% endhint %}

**Example:** Check that the student's ball started at the origin and is moving to the right:

**When Condition:** `t == 1`

**Success Condition:**

```python
initial_pos = frame(0).ball_pos
current_pos = ball.pos
exists('ball') and equals(initial_pos, vec(0, 0)) and current_pos.x > 0
```

## Adding Hints

Each goal can have one or more hints that appear to students when a goal is failing. Hints help guide students toward the correct answer without giving it away.

To add a hint, click the **+** button in the Hints section of the goal editor. Each hint has two parts:

* **Show when (optional)** — a Python expression (same as Success Condition) that returns `True` to show the hint. Leave blank to always show the hint when the goal is failing.
* **Hint text** — the message shown to the student.

The hint editor is part of the main goal editor — see the screenshot above.

Hints appear below the goal description whenever the goal is in a **FAIL** or **ERROR** state. Multiple hints can be active at once if their conditions are all true.

**Example:** A goal checks that the student set the correct initial velocity. You can add a hint that only appears if the variable exists but the value is wrong:

| Field     | Code                                                                 |
| --------- | -------------------------------------------------------------------- |
| Show when | `exists('velocity') and not close(mag(velocity), 10, 0.5)`           |
| Hint text | `Check the magnitude of your velocity vector — it should be 10 m/s.` |

And a second hint with no condition that always shows when failing:

| Field     | Code                                                                    |
| --------- | ----------------------------------------------------------------------- |
| Show when | *(empty — always shows)*                                                |
| Hint text | `Make sure you defined a variable called 'velocity' in the Setup Code.` |

The `exists()` helper is particularly useful in hint conditions — you can show different hints depending on whether the student has defined the variable at all.

## Tips When Writing Goals

* Use `exists('varname')` instead of checking `globals()` — it's simpler and works the same way.
* Use `close()` for any floating-point comparisons. Direct `==` on computed numbers will often fail due to rounding.
* Use `equals()` for all vector comparisons — never use `==` with vectors.
* Use `try-except` sparingly — the helper functions (`exists`, `has_attr`, `close`, `between`) are designed to be safe and won't throw errors in common cases.
* Test your goals by running the simulation yourself and verifying the goal turns green with the correct code and red with incorrect code.
* Use `print()` in goal code to print debug output to the Console while you're developing the goal.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.tychos.org/docs/for-teachers/writing-goals.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
