Navegar · Disparar · Encadenar · Avanzado Gana insignias de habilidad 0 / 6 ganadas
Antes de empezar
Configuración en MakeCode
Pasos de configuración
  1. Prepara el campo. Coloca el tapete de Velocity Arena sobre una superficie plana y dura. Necesitarás: tu Cutebot Pro, un micro:bit, la pelota chillona y la pelota de goma. Mantén ambas en el campo — la pelota de goma se usa en el Reto 3 y tendrá un papel más grande en la Temporada 2.
  2. Abre MakeCode. Ve a makecode.microbit.org en un navegador. Haz clic en Nuevo Proyecto y nómbralo SkillsArena.
  3. Agrega la extensión Cutebot Pro. Haz clic en el mosaico Extensiones de la caja de bloques (o engrane ⚙ → Extensiones). Escribe cutebot en la barra de búsqueda y haz clic en la tarjeta Cutebot Pro para agregarla. Solo necesitas hacer esto una vez por proyecto. Después de agregarla, aparecerá una categoría Cutebot Pro en la caja de herramientas.
  4. Cambia a JavaScript. Haz clic en el interruptor JavaScript de la barra superior (junto a la etiqueta Bloques). Esto cambia el editor al modo de texto.
  5. Borra el código predeterminado. Haz clic dentro del editor, presiona Ctrl+A (o ⌘A en Mac) para seleccionar todo, luego presiona Delete.
  6. Pega el código inicial. Haz clic en el botón Copiar Plantilla de abajo — copia todo el código inicial a tu portapapeles con un solo clic. Luego pégalo en MakeCode con Ctrl+V (o ⌘V en Mac). Las cuatro estadísticas estarán en 0 — eso es normal. Las llenarás en el siguiente paso.
  7. Ingresa las estadísticas de tu equipo. Cerca del inicio del código, busca el bloque STUDENT ZONE A. Reemplaza cada 0 con los valores reales de tu equipo. SPEED + POWER + ENDURANCE + TURNING deben sumar exactamente 20.
  8. Carga al micro:bit. Haz clic en Download y sigue las indicaciones para copiar el archivo a tu micro:bit. El LED desplazará tu motorPower y chargePower, luego mostrará 1 — estás listo. Vuelve a cargar cada vez que cambies el código. Las estadísticas solo surten efecto después de una nueva carga.
Cómo funciona el programa
Botones
B Recorre los retos 1 → 2 → 3 → 4 → 5 → 6 → 1…
El número aparece en el LED para que sepas cuál está seleccionado.
A Ejecuta la función del reto seleccionado.
Parpadea una flecha, corre el código y luego aparece un ✓ cuando termina.
A + B Paro de emergencia — los motores se detienen de inmediato y aparece una X en el LED.
Úsalo cada vez que el robot se salga de curso.
Secuencia LED de arranque
En cada carga, el robot muestra: P [motorPower] → K [chargePower] → 1
Esto confirma que tus estadísticas se cargaron correctamente. P es la potencia de manejo, K es la fuerza de pateo.
Si ves P0, regresa y llena tus estadísticas en la Zona A.
Tu tarea — funciones auxiliares de la Zona B
drive(n) Avanza en línea recta exactamente n casillas de la cuadrícula. 1 casilla ≈ 27 cm.
turnRight() Gira 90° en sentido horario. Mirando hacia y=0 → ahora mirando hacia x=8.
turnLeft() Gira 90° en sentido antihorario. Mirando hacia y=0 → ahora mirando hacia x=0.
kick() Dispara una ráfaga de potencia usando tu estadística Power. Fuerza = POWER². Luego se detiene.

Busca la función vacía de tu reto en STUDENT ZONE B y complétala usando estas cuatro funciones auxiliares. Carga después de cada cambio y presiona A para probar.

Código inicial — skillsCode.ts
Pega todo este bloque en el editor de JavaScript de MakeCode. Luego reemplaza los 0 con las estadísticas de tu equipo en STUDENT ZONE A.
/**
 * ═══════════════════════════════════════════════════════════════
 *  VELOCITY ARENA — Skills Arena Base Program
 *  micro:bit + Cutebot Pro   |   8×4 ft field   |   8×4 grid
 * ═══════════════════════════════════════════════════════════════
 *
 *  HOW TO USE
 *  ───────────
 *  1. Enter your team stats in STUDENT ZONE A below.
 *     S + P + E + T must add up to exactly 20.
 *
 *  2. Write your code inside the challenge function you are
 *     working on in STUDENT ZONE B below.
 *
 *  3. Flash the program to your micro:bit.
 *
 *  4. Press B to select a challenge (the number shows on screen).
 *     Press A to run it.
 *     Press A+B at any time for an emergency stop.
 *
 *  HELPER FUNCTIONS (call these inside your challenge functions)
 *  ─────────────────────────────────────────────────────────────
 *  drive(n)       move straight forward exactly n grid squares
 *                 1 grid square = 10.75 in = ~27 cm = 1 block
 *
 *  turnRight()    rotate exactly 90° clockwise in place
 *  turnLeft()     rotate exactly 90° counter-clockwise in place
 *
 *  kick()         burst of power from your Power stat, then stop
 *                 force = chargePower = POWER²  (0–100)
 *
 *  FIELD COORDINATE SYSTEM
 *  ────────────────────────
 *  x: 0 (left wall) → 8 (right wall)       8 columns
 *  y: 0 (top edge)  → 4 (bottom edge)      4 rows
 *  Labels on mat:  x = 1–7,  y = 1–3  (interior grid lines)
 *  Left  goal zone: x = 0–1, y = 1–3
 *  Right goal zone: x = 7–8, y = 1–3
 *  Ball at center:  (4, 2)
 * ═══════════════════════════════════════════════════════════════
 */


// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  ★  STUDENT ZONE A  ★   ENTER YOUR TEAM STATS HERE
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//
//  RULES:  each stat ≥ 1  |  POWER ≤ 10  |  S + P + E + T = 20
//
//  SPEED      how fast the bot drives
//  POWER      how hard it kicks  (chargePower = POWER²)
//  ENDURANCE  how long it stays fast in a match
//  TURNING    how sharply it steers in a match
//
let SPEED     = 0   // ← YOUR VALUE HERE
let POWER     = 0   // ← YOUR VALUE HERE
let ENDURANCE = 0   // ← YOUR VALUE HERE
let TURNING   = 0   // ← YOUR VALUE HERE
// ── CHECK: SPEED + POWER + ENDURANCE + TURNING must equal 20 ──


// ── CALCULATED VALUES  (read-only — do not change) ─────────────
let motorPower  = SPEED * 100 / 20             // drive speed %   (0–85)
let chargePower = Math.min(POWER * POWER, 100) // kick force %    (0–100)
let alignMs     = Math.max(60, 220 - TURNING * 16) // aim time ms
let DECAY_RATE  = 20 - ENDURANCE               // endurance decay rate


// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  DRIVE TUNING
//  Every drive() call spends ~100–150 ms overcoming static friction
//  and ramping to full speed before the bot moves a meaningful distance.
//  That startup overhead is constant — independent of how far you drive.
//  startupMs compensates for it.  unitMs controls speed per square.
//
//  Why tune startupMs FIRST?
//  drive(1) = 1 × unitMs + startupMs — startupMs dominates a short run,
//  so drive(1) isolates it cleanly.  Once startupMs is fixed, unitMs
//  only scales the per-square portion and drive(6) is easy to dial in.
//
//  Tuning order:
//    1. Run drive(1) (challenge 1, press B). Short? +20 to startupMs.
//       Overshot? −20.  Repeat until it lands on exactly 1 square.
//    2. Run drive(6). Short? +20 to unitMs.  Overshot? −20.
//       You'll land around unitMs = 590–620.
//    3. Test drive(4) and drive(2) — they should now land without re-tuning.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
let unitMs    = 600   // ← tune SECOND with drive(6) — expect 590–620
let startupMs = 100   // ← tune FIRST with drive(1)  — expect 80–140


// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  CHALLENGE SELECTOR
//  B = next challenge (1 → 2 → … → 9 → 1 → ...)
//  A = run the selected challenge
//
//  CHALLENGE 7 — POWER CONTROL LAB has a built-in power menu:
//    Press A on #7  →  enters power-select mode (LED shows "3")
//    Press B        →  cycles: 3 → 6 → 9 → your POWER → 3 …
//    Press A        →  runs the kick at the displayed power
//    Press A+B      →  emergency stop / exit power-select mode
//
//  CHALLENGE 8 — PRECISION PARKING has two variants:
//    Press A on #8  →  runs Precision Parking (variant 1, default)
//    Press B on #8  →  switches to variant 2 (LED shows "P2")
//    Press A        →  runs Precision Parking 2
//    Press B again  →  advances to challenge 9 (resets to variant 1)
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
let currentChallenge = 1
let inLabSelect = false   // true while choosing power for challenge 7
let labPowerIdx = 0       // 0=P3  1=P6  2=P9  3=your POWER
let parkVariant = 1       // challenge 8: 1=precisionParking  2=precisionParking2

// Returns the kick POWER value for the currently selected lab trial.
function labPowerValue(): number {
    if (labPowerIdx === 0) { return 3 }
    if (labPowerIdx === 1) { return 6 }
    if (labPowerIdx === 2) { return 9 }
    return POWER  // 4th option restores your actual stat
}

input.onButtonPressed(Button.B, function () {
    if (inLabSelect) {
        // Challenge 7: cycle through lab power options
        labPowerIdx = (labPowerIdx + 1) % 4
        basic.showNumber(labPowerValue())
    } else if (currentChallenge === 8 && parkVariant === 1) {
        // Challenge 8: switch to variant 2
        parkVariant = 2
        basic.showString("P2")
    } else {
        // Normal challenge cycling (also exits #8 variant 2 back to variant 1)
        if (currentChallenge === 8) { parkVariant = 1 }
        currentChallenge = (currentChallenge % 9) + 1
        basic.showNumber(currentChallenge)
    }
})

input.onButtonPressed(Button.A, function () {
    // ── Challenge 7: enter power-select mode on first press ──────
    if (currentChallenge === 7 && !inLabSelect) {
        inLabSelect = true
        basic.showNumber(labPowerValue())   // show current selection
        return
    }
    // ── Challenge 7: run at selected power on second press ───────
    if (inLabSelect) {
        inLabSelect = false
        let p = labPowerValue()
        let savedCharge = chargePower
        chargePower = Math.min(p * p, 100)  // apply POWER² to lab value
        basic.showArrow(ArrowNames.East)
        powerControlLab()
        chargePower = savedCharge           // restore team's real chargePower
        CutebotPro.stopImmediately(CutebotProMotors.ALL)
        basic.showIcon(IconNames.Yes)
        return
    }
    // ── Challenge 8: run variant 1 or 2 ─────────────────────────
    if (currentChallenge === 8) {
        basic.showArrow(ArrowNames.East)
        if (parkVariant === 2) {
            precisionParking2()
        } else {
            precisionParking()
        }
        parkVariant = 1                         // reset to default after each run
        CutebotPro.stopImmediately(CutebotProMotors.ALL)
        basic.showIcon(IconNames.Yes)
        return
    }
    // ── All other challenges ─────────────────────────────────────
    basic.showArrow(ArrowNames.East)
    if      (currentChallenge === 1) { deadStraight()    }
    else if (currentChallenge === 2) { rightAngle()      }
    else if (currentChallenge === 3) { straightShot()    }
    else if (currentChallenge === 4) { cornerKickLeft()  }
    else if (currentChallenge === 5) { cornerKickRight() }
    else if (currentChallenge === 6) { twoBallCombo()    }
    else if (currentChallenge === 9) { bossLevel()        }
    CutebotPro.stopImmediately(CutebotProMotors.ALL)
    basic.showIcon(IconNames.Yes)
})

// A + B  →  emergency stop (works at any time; also exits power-select mode)
input.onButtonPressed(Button.AB, function () {
    inLabSelect = false
    parkVariant = 1
    CutebotPro.stopImmediately(CutebotProMotors.ALL)
    CutebotPro.pwmCruiseControl(0, 0)
    basic.showLeds(`
        # . . . #
        . # . # .
        . . # . .
        . # . # .
        # . . . #`)
})


// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  HELPER FUNCTIONS
//  Call these inside your challenge functions below.
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

/**
 * drive(n)
 * Move straight forward for n grid squares.
 * Speed comes from your SPEED stat (motorPower).
 * Total motor-on time = unitMs × n + startupMs milliseconds.
 * Tune both values in the DRIVE TUNING section above.
 *
 * Example:  drive(3) with unitMs=500, startupMs=0  →  1500 ms
 */
function drive(units: number): void {
    CutebotPro.pwmCruiseControl(motorPower, motorPower)
    basic.pause(unitMs * units + startupMs)
    CutebotPro.stopImmediately(CutebotProMotors.ALL)
    basic.pause(100)
}

/**
 * turnRight()
 * Rotate the bot 90° clockwise (viewed from above).
 *
 * Facing toward y=0 (-y)  →  now facing toward x=8 (+x)
 * Facing toward x=8 (+x)  →  now facing toward y=4 (+y)
 * Facing toward y=4 (+y)  →  now facing toward x=0 (-x)
 * Facing toward x=0 (-x)  →  now facing toward y=0 (-y)
 */
function turnRight(): void {
    CutebotPro.trolleySteering(CutebotProTurn.RightInPlace, 90)
    basic.pause(150)
}

/**
 * turnLeft()
 * Rotate the bot 90° counterclockwise (viewed from above).
 *
 * Facing toward y=0 (-y)  →  now facing toward x=0 (-x)
 * Facing toward x=0 (-x)  →  now facing toward y=4 (+y)
 * Facing toward y=4 (+y)  →  now facing toward x=8 (+x)
 * Facing toward x=8 (+x)  →  now facing toward y=0 (-y)
 */
function turnLeft(): void {
    CutebotPro.trolleySteering(CutebotProTurn.LeftInPlace, 90)
    basic.pause(150)
}

/**
 * kick()
 * Fire a power burst toward whatever the bot is facing.
 * Force comes from your POWER stat:  chargePower = POWER²
 *
 * Example:  POWER = 7  →  chargePower = 49%
 *           POWER = 10 →  chargePower = 100%  (max)
 */
function kick(): void {
    CutebotPro.pwmCruiseControl(chargePower, chargePower)
    basic.pause(400)
    CutebotPro.stopImmediately(CutebotProMotors.ALL)
}


// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  ★  STUDENT ZONE B  ★   WRITE YOUR CHALLENGE FUNCTIONS BELOW
//
//  Each function is called when you press A with that challenge
//  selected. Fill in the body using drive(), turnLeft(),
//  turnRight(), and kick().
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


// ── Challenge 1 — DEAD STRAIGHT ──────────────────────────────────
//  Start:  (1, 0)  facing right (+x direction)
//  Target: (7, 0)
//  Task:   Drive 6 grid squares in a straight line.  No turns.
//
function deadStraight(): void {
    // YOUR CODE HERE
    // Hint: call drive(?) — how many squares to the target?

}


// ── Challenge 2 — RIGHT ANGLE ─────────────────────────────────────
//  Start:  (1, 0)  facing down (+y direction)
//  Target: (6, 3)
//  Task:   Drive 3 squares → turn right 90° → drive 5 squares.
//
function rightAngle(): void {
    // YOUR CODE HERE
    // Hint: drive(?) → turnRight() → drive(?)

}


// ── Challenge 3 — STRAIGHT SHOT ──────────────────────────────────
//  Start:  (6, 2)  facing LEFT (toward x = 0)
//  Ball:   (4, 2)  — already lined up with the bot
//  Goal:   Left goal zone  (x = 0–1, y = 1–3)
//  Task:   Drive to the ball, then kick it into the left goal.
//          No turns needed — the shot is already aligned.
//
function straightShot(): void {
    // YOUR CODE HERE
    // Hint: drive(?) to reach the ball, then kick()

}


// ── Challenge 4 — CORNER KICK: LEFT TURN ──────────────────────────
//  Start:  (3, 4)  facing (3, 0)
//  Ball:   (2, 2)
//  Goal:   Left goal zone  (x = 0–1, y = 1–3)
//  Leg 1 is fixed: drive(2) — Δy = 4−2 = 2 squares.
//  Leg 2 is the variable: stop SHORT of the ball and let
//  kick() close the gap. Try drive(0.5), drive(0.7), etc.
//  Too close → overshoots past x=0. Too far → falls short of x=1.
//
function cornerKickLeft(): void {
    // YOUR CODE HERE
    // drive(2) → turnLeft() → drive(???) → kick()

}


// ── Challenge 5 — CORNER KICK: RIGHT SIDE ────────────────────────
//  Start:  (4, 0)  facing down (+y direction)
//  Ball:   (6, 2)
//  Goal:   Right goal zone  (x = 7–8, y = 1–3)
//  Task:   Navigate to the ball from an angle, then kick right.
//
function cornerKickRight(): void {
    // YOUR CODE HERE
    // Hint: drive(?) → turnRight() → drive(?) → kick()

}


// ── Challenge 6 — TWO-BALL COMBO ─────────────────────────────────
//  Start:  (4, 0)  facing south (+y direction)
//  Ball A: (2, 2)  → left  goal zone (x = 0–1, y = 1–3)
//  Ball B: (6, 2)  → right goal zone (x = 7–8, y = 1–3)
//  Task:   In one program, kick Ball A into the left goal and
//          Ball B into the right goal. No hints — plan your
//          own route and decide which ball you kick first.
//
function twoBallCombo(): void {
    // YOUR CODE HERE
    // Plan on paper first. Ball A at (2,2), Ball B at (6,2).
    // Hint: there is no single correct route — choose your order,
    // draw the path, count every square, count every turn.

}


// ── Challenge 7 — POWER CONTROL LAB ──────────────────────────────
//  Start:  (3, 2)  facing west (toward x = 0)
//  Ball:   (2, 2)  — reset before every run
//  Task:   Run the same drive(1) + kick() three times using
//          POWER = 3, POWER = 6, and POWER = 9.
//          Record ball distance each time. Is it linear?
//
function powerControlLab(): void {
    // YOUR CODE HERE
    // Hint: drive(1) to the ball, then kick()
    // Change POWER in STUDENT ZONE A and re-flash between runs.

}


// ── Challenge 8 — PRECISION PARKING ──────────────────────────────
//  Start:  (0, 0)  facing east (+x direction)
//  Target: taped box at (5, 3), final facing = WEST
//  Task:   Navigate to the box by any route. Stop inside it
//          facing exactly west. Sketch your path first —
//          mark your facing direction after every call.
//
function precisionParking(): void {
    // YOUR CODE HERE
    // No hint sequence — your path is your design.
    // Remember: to end up facing WEST you must plan the
    // approach direction and your final turn carefully.

}


// ── Challenge 8 variant 2 — PRECISION PARKING 2 ───────────────────
//  Same concept as Precision Parking — different start, target, or
//  constraint. Define your own variant here.
//
function precisionParking2(): void {
    // YOUR CODE HERE

}


// ── Challenge 9 — BOSS LEVEL ─────────────────────────────────────
//  Start:  (4, 0)  facing south (+y direction)
//  Ball:   (4, 2)  — center of field
//  Cone:   (4, 1)  — directly in the way; CANNOT pass through
//  Task:   Route around the cone, reach the ball, kick it into
//          either goal, then drive to the parking zone at
//          x = 0–1, y = 0. Score: 1 pt per phase (max 3).
//
function bossLevel(): void {
    // YOUR CODE HERE
    // Write your plan in 4 phases before you code:
    //   Phase 1 — detour around cone at (4,1)
    //   Phase 2 — navigate to ball at (4,2)
    //   Phase 3 — kick into left or right goal
    //   Phase 4 — drive to parking zone (x=0–1, y=0)

}

// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
//  STARTUP
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
CutebotPro.stopImmediately(CutebotProMotors.ALL)
CutebotPro.pwmCruiseControl(0, 0)
basic.pause(100)

// Show a quick stat summary: motorPower then chargePower
basic.showString("P" + motorPower)
basic.pause(400)
basic.showString("K" + chargePower)
basic.pause(400)

// Show which challenge is loaded
basic.showNumber(currentChallenge)
Habilidad
10:00
restante