<!-- Generated from applied-computing-au vic/unit3-4/sat/C07-2026/C07-Reference-Godot by port-reference-godot-to-wiki.py — do not hand-edit; re-run the port. -->
# Validation Techniques — GDScript

**Skill codes on this page**

| Code | Level | Skill |
|---|---|---|
| C713 | 1–2 | identifies input data for validation |
| C723 | 3–4 | existence checking |
| C724 | 3–4 | type checking |
| C725 | 3–4 | range checking |
| C735 | 5–6 | two of the three checks |
| C745 | 7–8 | all three checks |
| C753 ✍️ | 9–10 | ALL input data, plus reasonableness and completeness |

**Definition.** *Validation* checks the reasonableness of data inputs, while the program runs. It is not testing (testing happens to the finished program; validation happens to every input, every run). The three named checks: **existence** (was anything entered?), **type** (is it the right kind of value?), **range** (is it within acceptable limits?).

**This ladder is not a checklist.** The individual checks are separate codes only at level 3–4. Above that, single codes stand for the *combination*: C735 = two checks working, C745 = all three, C753 = all inputs plus reasonableness and completeness. Consistency qualifiers decide the band: *some inconsistencies* (5–6) → *minor* (7–8) → *none* (9–10).

**In the validation** Part B you add existence/type/range checks to the picked feature's real inputs, label them as you go, and **run a test to show a check actually fires** — a validation that never catches a bad value is scenery.

## C713 — Identify the inputs

Level 1–2 is a written list of what needs validating:

```gdscript
# C713 — inputs to validate in this feature:
#   team name   — NameField (LineEdit), free text
#   zone        — ZoneSpinBox (SpinBox), whole number
#   top-up      — TopUpField (LineEdit), dollars and cents
```

**Earns the tick:** every user-facing input of the picked feature named. (Choosing constrained controls — a SpinBox can't receive letters — is good design, but the codes below want checks *in code*.)

## C723 — Existence check

**Definition.** A test to see if a value has been entered as input or not.

```gdscript
# club-ladder — app.gd
func add_team(raw_name: String) -> void:
    var team_name := raw_name.strip_edges()
    if team_name == "":                        # C723 — existence: nothing entered
        message_label.text = "Type a team name first."
        return
```

Note the real-world details worth copying: `strip_edges()` first (spaces are not a name), and an error message that says **what to do**, not just "invalid".

## C724 — Type check

**Definition.** A test to see if a value is of the specified data type or structure.

GDScript's idioms — check *before* converting, or the conversion itself is the crash:

```gdscript
# C724 — type: is the text actually a number?
if not $TopUpField.text.is_valid_float():
    $MessageLabel.text = "Enter an amount in dollars, like 10.50"
    return
var amount := float($TopUpField.text)
```

`String.is_valid_int()` and `is_valid_float()` are the workhorses. The `is` keyword type-checks objects — real use in tile-matching-game: `if child is Card:` (`board.gd`).

## C725 — Range check

**Definition.** Tests to see if a value is within a given range of acceptable values.

```gdscript
# C725 — range: zones only run 1..3, amounts have a ceiling
if zone < 1 or zone > MAX_ZONE:
    $MessageLabel.text = "Zone must be 1 to %d" % MAX_ZONE
    return
if amount <= 0.0 or amount > MAX_TOP_UP:
    $MessageLabel.text = "Top-up must be between $0.01 and $%.2f" % MAX_TOP_UP
    return
```

The limits are **named constants** (`MAX_ZONE`, `MAX_TOP_UP`) — that's C7-1 working for C7-3. Clamping (`maxi(1, column_count)` in club-ladder's `board.gd`) is the accept-and-correct cousin of reject-and-message; for user input, prefer the message — silent correction hides the user's mistake from them.

## C735 and C745 — The checks working together

One validated input, all three checks, in the order that cannot crash — existence, then type, then range:

```gdscript
# C745 — full validation of the top-up amount (existence → type → range)
func validate_top_up(raw: String) -> String:
    var text := raw.strip_edges()
    if text == "":                                   # C723 — existence
        return "Enter an amount first."
    if not text.is_valid_float():                    # C724 — type
        return "Enter a number, like 10.50"
    var amount := float(text)
    if amount <= 0.0 or amount > MAX_TOP_UP:         # C725 — range
        return "Amount must be between $0.01 and $%.2f" % MAX_TOP_UP
    return ""   # empty string = valid
```

C735 (5–6) is two of these working on real inputs; C745 (7–8) is all three, with at most *minor* inconsistencies. **Error messages are part of the standard** — "Must be between 8 and 72 in length" beats "Invalid input" because it tells the user how to fix it (that Discord message was the class's best-in-show in the Validation Detective activity).

## C753 — All inputs, plus reasonableness and completeness ✍️

Level 9–10 widens the lens from single fields to the **whole data set**:

- **All relevant inputs** validated — not just the easy one.

- **Completeness** — is anything missing? Real example: `deck.gd` skips half-empty CSV rows with `if row.size() < 2: continue` (tile-matching-game).

- **Reasonableness** — can these values be true *together*? The model is club-ladder's cross-check:

```gdscript
# club-ladder — app.gd
## In a competition where teams play each other, one team's win is always      # C753 ✍️
## another's loss — so total wins must equal total losses. Worth saying on
## screen rather than quietly showing figures that cannot be true.
func totals_warning() -> String:
    if total_wins == total_losses:
        return ""
    return "Check your data: %d wins recorded, but %d losses." % [total_wins, total_losses]
```

**Earns the tick:** every input covered, a completeness check on incoming records, at least one reasonableness rule that crosses fields — and (✍️) the rule *written down*: work out what rules **your** data has to obey, and say so in a comment.

**Then prove it fires.** End the validation task by entering a bad value and showing the message appear — the package script asks for exactly this test run.

## Check Your Understanding

1. Name the three validation checks and their level 3–4 codes.

>| ### Answer
>| Existence C723, type C724, range C725.

2. Why are C735 and C745 single codes rather than lists of checks?

>| ### Answer
>| Above level 3–4 the descriptor rewards the *combination* — two checks working (C735), then all three (C745) — with consistency qualifiers deciding the band.

3. What makes "Must be between 8 and 72 in length" a better error message than "Invalid input"?

>| ### Answer
>| It tells the user exactly how to fix the problem — specific beats generic.

4. Give an example of a reasonableness check (C753).

>| ### Answer
>| A cross-field rule — e.g. club-ladder's "total wins must equal total losses"; values that are individually fine but cannot be true together.
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9