mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
test: make linear pathing suite manifest-driven
This commit is contained in:
parent
e4fc557aac
commit
84a452d5c9
3 changed files with 204 additions and 88 deletions
123
tools/pathing_live_common.sh
Normal file
123
tools/pathing_live_common.sh
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
manifest_cases_for_query() {
|
||||
local manifest_path="$1"
|
||||
local family_csv="$2"
|
||||
|
||||
python3 - "$manifest_path" "$family_csv" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as handle:
|
||||
manifest = json.load(handle)
|
||||
|
||||
families = {item for item in sys.argv[2].split(",") if item}
|
||||
for row in manifest:
|
||||
if row["family"] in families and row["movement_mode"] == "sprint" and row["momentum_ticks"] == 12:
|
||||
print(row["case_id"])
|
||||
PY
|
||||
}
|
||||
|
||||
manifest_case_json() {
|
||||
local manifest_path="$1"
|
||||
local case_id="$2"
|
||||
|
||||
python3 - "$manifest_path" "$case_id" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
with open(sys.argv[1], "r", encoding="utf-8") as handle:
|
||||
manifest = json.load(handle)
|
||||
|
||||
case_id = sys.argv[2]
|
||||
row = next(row for row in manifest if row["case_id"] == case_id)
|
||||
print(json.dumps(row))
|
||||
PY
|
||||
}
|
||||
|
||||
record_live_result() {
|
||||
local results_path="$1"
|
||||
local case_json="$2"
|
||||
local live_result="$3"
|
||||
local log_path="$4"
|
||||
|
||||
python3 - "$results_path" "$case_json" "$live_result" "$log_path" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
row = json.loads(sys.argv[2])
|
||||
record = {
|
||||
"case_id": row["case_id"],
|
||||
"bucket_id": row["bucket_id"],
|
||||
"world_recipe_id": row["world_recipe_id"],
|
||||
"expected_result": row["expected_result"],
|
||||
"live_result": sys.argv[3],
|
||||
"log_path": sys.argv[4],
|
||||
}
|
||||
|
||||
with open(sys.argv[1], "a", encoding="utf-8") as handle:
|
||||
handle.write(json.dumps(record) + "\n")
|
||||
PY
|
||||
}
|
||||
|
||||
run_test() {
|
||||
local name="$1"
|
||||
local start_x="$2" start_y="$3" start_z="$4"
|
||||
local dest_x="$5" dest_y="$6" dest_z="$7"
|
||||
|
||||
TEST_NUM=$((TEST_NUM + 1))
|
||||
echo ""
|
||||
echo "=== TEST $TEST_NUM: $name ==="
|
||||
echo " Start: ($start_x, $start_y, $start_z) -> Dest: ($dest_x, $dest_y, $dest_z)"
|
||||
|
||||
mcc-cmd "respawn" 2>/dev/null || true
|
||||
sleep 0.5
|
||||
mc-rcon "gamemode creative CursorBot" >/dev/null 2>&1
|
||||
sleep 0.3
|
||||
mc-rcon "tp CursorBot ${start_x}.5 ${start_y} ${start_z}.5" >/dev/null 2>&1
|
||||
sleep 2
|
||||
mc-rcon "gamemode survival CursorBot" >/dev/null 2>&1
|
||||
sleep 1
|
||||
|
||||
: > "$LOG"
|
||||
sleep 0.5
|
||||
|
||||
mcc-cmd "pathfind $dest_x $dest_y $dest_z"
|
||||
sleep 8
|
||||
|
||||
local a_star_result
|
||||
a_star_result=$(grep -a '\[A\*\]' "$LOG" | head -3 | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local path_exec
|
||||
path_exec=$(grep -a '\[PathExec\]' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local path_mgr
|
||||
path_mgr=$(grep -a '\[PathMgr\]' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local nav_segs
|
||||
nav_segs=$(grep -a '\[Navigate\].*seg' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local physics_line
|
||||
physics_line=$(grep -a '\[Physics\]' "$LOG" | tail -1 | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local result="invalid_live_case"
|
||||
if echo "$path_mgr" | grep -q "complete"; then
|
||||
result="pass"
|
||||
elif echo "$a_star_result" | grep -q "Failed"; then
|
||||
result="reject"
|
||||
elif echo "$path_mgr" | grep -q "Replan failed\|Giving up"; then
|
||||
result="fail"
|
||||
elif echo "$path_exec" | grep -q "FAILED"; then
|
||||
result="fail"
|
||||
fi
|
||||
LAST_RESULT="$result"
|
||||
|
||||
echo " A*: $a_star_result"
|
||||
echo " Segments: $nav_segs"
|
||||
echo " Exec: $(echo "$path_exec" | tail -3)"
|
||||
echo " Manager: $(echo "$path_mgr" | tail -2)"
|
||||
echo " Physics: $physics_line"
|
||||
echo " RESULT: $LAST_RESULT"
|
||||
|
||||
RESULTS="${RESULTS}TEST $TEST_NUM ($name): $LAST_RESULT\n"
|
||||
}
|
||||
|
|
@ -1,107 +1,78 @@
|
|||
#!/usr/bin/env bash
|
||||
# Automated parkour jump test for MCC pathfinding
|
||||
# Usage: source tools/mcc-env.sh && bash tools/test-parkour.sh
|
||||
#
|
||||
# Prerequisites:
|
||||
# - MCC connected with FileInput mode
|
||||
# - CursorBot is OP
|
||||
# - Server at 1.21.11-Vanilla
|
||||
|
||||
set -euo pipefail
|
||||
source "$(dirname "$0")/mcc-env.sh"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
source "$REPO_ROOT/tools/mcc-env.sh"
|
||||
source "$REPO_ROOT/tools/pathing_live_common.sh"
|
||||
|
||||
MANIFEST="$REPO_ROOT/tools/pathing_data/canonical-live-cases.json"
|
||||
RESULTS_FILE="${RESULTS_FILE:-/tmp/mcc-debug/pathing-live-results.jsonl}"
|
||||
LOG="/tmp/mcc-debug/mcc-debug.log"
|
||||
RESULTS=""
|
||||
TEST_NUM=0
|
||||
LAST_RESULT="invalid_live_case"
|
||||
|
||||
run_test() {
|
||||
local name="$1"
|
||||
local start_x="$2" start_y="$3" start_z="$4"
|
||||
local dest_x="$5" dest_y="$6" dest_z="$7"
|
||||
|
||||
TEST_NUM=$((TEST_NUM + 1))
|
||||
echo ""
|
||||
echo "=== TEST $TEST_NUM: $name ==="
|
||||
echo " Start: ($start_x, $start_y, $start_z) -> Dest: ($dest_x, $dest_y, $dest_z)"
|
||||
|
||||
# Respawn if dead, set creative, tp, then survival
|
||||
mcc-cmd "respawn" 2>/dev/null
|
||||
sleep 0.5
|
||||
mc-rcon "gamemode creative CursorBot" >/dev/null 2>&1
|
||||
sleep 0.3
|
||||
mc-rcon "tp CursorBot ${start_x}.5 ${start_y} ${start_z}.5" >/dev/null 2>&1
|
||||
sleep 2
|
||||
mc-rcon "gamemode survival CursorBot" >/dev/null 2>&1
|
||||
sleep 1
|
||||
|
||||
# Clear log
|
||||
: > "$LOG"
|
||||
sleep 0.5
|
||||
|
||||
# Execute pathfind
|
||||
mcc-cmd "pathfind $dest_x $dest_y $dest_z"
|
||||
sleep 8
|
||||
|
||||
# Analyze result
|
||||
local a_star_result
|
||||
a_star_result=$(grep -a '\[A\*\]' "$LOG" | head -3 | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local path_exec
|
||||
path_exec=$(grep -a '\[PathExec\]' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local path_mgr
|
||||
path_mgr=$(grep -a '\[PathMgr\]' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
local nav_segs
|
||||
nav_segs=$(grep -a '\[Navigate\].*seg' "$LOG" | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
# Get final position
|
||||
local physics_line
|
||||
physics_line=$(grep -a '\[Physics\]' "$LOG" | tail -1 | sed 's/\x1b\[[0-9;]*m//g')
|
||||
|
||||
# Check success/failure
|
||||
local result="UNKNOWN"
|
||||
if echo "$path_mgr" | grep -q "complete"; then
|
||||
result="PASS"
|
||||
elif echo "$path_mgr" | grep -q "Replan failed\|Giving up"; then
|
||||
result="FAIL"
|
||||
elif echo "$path_exec" | grep -q "FAILED"; then
|
||||
result="FAIL"
|
||||
elif echo "$a_star_result" | grep -q "Failed"; then
|
||||
result="NO_PATH"
|
||||
fi
|
||||
|
||||
echo " A*: $a_star_result"
|
||||
echo " Segments: $nav_segs"
|
||||
echo " Exec: $(echo "$path_exec" | tail -3)"
|
||||
echo " Manager: $(echo "$path_mgr" | tail -2)"
|
||||
echo " Physics: $physics_line"
|
||||
echo " RESULT: $result"
|
||||
|
||||
RESULTS="${RESULTS}TEST $TEST_NUM ($name): $result\n"
|
||||
if [[ "${1:-}" == "--list-cases" ]]; then
|
||||
manifest_cases_for_query "$MANIFEST" "linear"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$RESULTS_FILE")"
|
||||
: > "$RESULTS_FILE"
|
||||
|
||||
run_manifest_case() {
|
||||
local case_id="$1"
|
||||
local case_json
|
||||
case_json="$(manifest_case_json "$MANIFEST" "$case_id")"
|
||||
|
||||
read -r world_recipe start_x start_y start_z goal_x goal_y goal_z < <(
|
||||
python3 - "$case_json" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
row = json.loads(sys.argv[1])
|
||||
print(
|
||||
row["world_recipe_id"],
|
||||
row["start"]["x"],
|
||||
row["start"]["y"],
|
||||
row["start"]["z"],
|
||||
row["goal"]["x"],
|
||||
row["goal"]["y"],
|
||||
row["goal"]["z"],
|
||||
)
|
||||
PY
|
||||
)
|
||||
|
||||
local landing_block_y=$(( ${goal_y%.*} - 1 ))
|
||||
|
||||
case "$world_recipe" in
|
||||
linear-flat|linear-ascend|linear-descend)
|
||||
mc-rcon "fill 95 80 95 115 90 105 air" >/dev/null
|
||||
mc-rcon "fill 95 79 95 115 79 105 air" >/dev/null
|
||||
mc-rcon "setblock 100 79 100 stone" >/dev/null
|
||||
mc-rcon "setblock ${goal_x%.*} ${landing_block_y} ${goal_z%.*} stone" >/dev/null
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported world recipe for test-parkour.sh: $world_recipe" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
|
||||
run_test "$case_id" "${start_x%.*}" "${start_y%.*}" "${start_z%.*}" "${goal_x%.*}" "${goal_y%.*}" "${goal_z%.*}"
|
||||
record_live_result "$RESULTS_FILE" "$case_json" "$LAST_RESULT" "$LOG"
|
||||
}
|
||||
|
||||
echo "========================================"
|
||||
echo " MCC Parkour Jump Test Suite"
|
||||
echo "========================================"
|
||||
|
||||
# Flat gap tests (same Y level)
|
||||
run_test "Gap 1 flat" 100 100 100 102 100 100
|
||||
run_test "Gap 2 flat" 100 100 102 103 100 102
|
||||
run_test "Gap 3 flat" 100 100 104 104 100 104
|
||||
run_test "Gap 4 flat" 100 100 106 105 100 106
|
||||
|
||||
# Ascend tests (+1Y)
|
||||
run_test "Gap 1 up +1" 100 100 108 102 101 108
|
||||
run_test "Gap 2 up +1" 100 100 110 103 101 110
|
||||
|
||||
# Descend tests (-1Y)
|
||||
run_test "Gap 1 down -1" 100 100 112 102 99 112
|
||||
run_test "Gap 2 down -1" 100 100 114 103 99 114
|
||||
|
||||
# Descend tests (-2Y)
|
||||
run_test "Gap 1 down -2" 100 100 94 102 98 94
|
||||
run_test "Gap 2 down -2" 100 100 92 103 98 92
|
||||
while IFS= read -r case_id; do
|
||||
run_manifest_case "$case_id"
|
||||
done < <(manifest_cases_for_query "$MANIFEST" "linear")
|
||||
|
||||
echo ""
|
||||
echo "========================================"
|
||||
|
|
|
|||
22
tools/tests/test_pathing_live_scripts.py
Normal file
22
tools/tests/test_pathing_live_scripts.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
import subprocess
|
||||
import unittest
|
||||
|
||||
|
||||
class PathingLiveScriptTests(unittest.TestCase):
|
||||
def test_test_parkour_lists_linear_canonical_cases(self) -> None:
|
||||
result = subprocess.run(
|
||||
["bash", "tools/test-parkour.sh", "--list-cases"],
|
||||
check=False,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
self.assertIn("linear-flat-sprint-mm12-gap5-dy0p0", result.stdout)
|
||||
self.assertIn("linear-ascend-sprint-mm12-gap2-dy1p0", result.stdout)
|
||||
self.assertNotIn("linear-flat-walk-mm12-gap5-dy0p0", result.stdout)
|
||||
self.assertNotIn("linear-flat-sprint-mm0-gap3-dy0p0", result.stdout)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue