From d465f83f70b82979075b0460ae2631c72c22b533 Mon Sep 17 00:00:00 2001 From: Greg Neighbors Date: Thu, 7 May 2026 10:42:24 -0400 Subject: [PATCH 1/2] Replace javafx.util.Pair with aima.core.util.datastructure.Pair in LabeledGraphTest The javafx.util.Pair import broke compilation on JDKs without bundled JavaFX (e.g. Zulu CA) and was removed from the JDK in Java 11. The project already exposes its own Pair with the constructor and equals semantics this test uses. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../java/aima/test/unit/environment/map2d/LabeledGraphTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java b/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java index 05306ee17f..6c186ec8d3 100644 --- a/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java +++ b/test/src/test/java/aima/test/unit/environment/map2d/LabeledGraphTest.java @@ -1,7 +1,7 @@ package aima.test.unit.environment.map2d; import aima.core.environment.map2d.LabeledGraph; -import javafx.util.Pair; +import aima.core.util.datastructure.Pair; import org.junit.Assert; import org.junit.Before; import org.junit.Test; From 0594e5b15c5ac5a95672ff683ca839ec6004a3ca Mon Sep 17 00:00:00 2001 From: Greg Neighbors Date: Thu, 7 May 2026 10:42:24 -0400 Subject: [PATCH 2/2] Add tests for DepthLimitedTreeSearch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Covers the algorithm's three return modes — solution, cutoff (null), and failure (empty list) — across the Romania map and binary-tree fixtures, plus a vacuum-world case that documents the non-minimal path DLS-tree produces when LEFT is a no-op at the leftmost square (no cycle check in tree search). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../DepthLimitedTreeSearchTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java diff --git a/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java b/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java new file mode 100644 index 0000000000..7adfc05c59 --- /dev/null +++ b/test/src/test/java/aima/test/unit/search/uninformed/DepthLimitedTreeSearchTest.java @@ -0,0 +1,97 @@ +package aima.test.unit.search.uninformed; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +import aima.core.environment.map2d.GoAction; +import aima.core.environment.map2d.SimplifiedRoadMapOfPartOfRomania; +import aima.core.environment.support.ProblemFactory; +import aima.core.environment.vacuum.VELocalState; +import aima.core.environment.vacuum.VacuumEnvironment; +import aima.core.search.api.Problem; +import aima.core.search.basic.uninformed.DepthLimitedTreeSearch; + +public class DepthLimitedTreeSearchTest { + + private List searchForActions(Problem problem, int limit) { + return new DepthLimitedTreeSearch(limit).apply(problem); + } + + @Test + public void testStartEqualsGoalReturnsTrivialSolution() { + Assert.assertEquals(Arrays.asList((String) null), + searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem( + SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.ARAD), 0)); + } + + @Test + public void testRomaniaAtSufficientDepth() { + Assert.assertEquals( + Arrays.asList(new GoAction(SimplifiedRoadMapOfPartOfRomania.SIBIU), + new GoAction(SimplifiedRoadMapOfPartOfRomania.FAGARAS), + new GoAction(SimplifiedRoadMapOfPartOfRomania.BUCHAREST)), + searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem( + SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 3)); + } + + @Test + public void testRomaniaAtInsufficientDepthReturnsCutoff() { + // Shortest Arad -> Bucharest is 3 actions, so a limit of 2 must hit cutoff. + Assert.assertNull(searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem( + SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 2)); + } + + @Test + public void testRomaniaAtZeroLimitNonGoalReturnsCutoff() { + Assert.assertNull(searchForActions(ProblemFactory.getSimplifiedRoadMapOfPartOfRomaniaProblem( + SimplifiedRoadMapOfPartOfRomania.ARAD, SimplifiedRoadMapOfPartOfRomania.BUCHAREST), 0)); + } + + @Test + public void testVacuumGoalAlreadyReachableInOneStep() { + // Single SUCK at the start state already cleans both squares — no LEFT no-op + // loop can come first, since SUCK is tried before RIGHT in the action order. + Assert.assertEquals(Arrays.asList(VacuumEnvironment.ACTION_SUCK), + searchForActions(ProblemFactory.getSimpleVacuumWorldProblem("A", + new VELocalState("A", VacuumEnvironment.Status.Dirty), + new VELocalState("B", VacuumEnvironment.Status.Clean)), 1)); + } + + @Test + public void testTreeSearchYieldsNonMinimalPathInVacuumWorld() { + // DLS-tree has no cycle check. With LEFT as a no-op at the leftmost position + // and action order [Left, Suck, Right], the first goal-reaching plan it finds + // at limit=5 walks the LEFT self-loop before turning right. + Assert.assertEquals( + Arrays.asList(VacuumEnvironment.ACTION_LEFT, VacuumEnvironment.ACTION_LEFT, + VacuumEnvironment.ACTION_LEFT, VacuumEnvironment.ACTION_RIGHT, + VacuumEnvironment.ACTION_SUCK), + searchForActions(ProblemFactory.getSimpleVacuumWorldProblem("A", + new VELocalState("A", VacuumEnvironment.Status.Clean), + new VELocalState("B", VacuumEnvironment.Status.Dirty)), 5)); + } + + @Test + public void testReachableBinaryTreeGoals() { + Assert.assertEquals(Arrays.asList((String) null), + searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "A"), 0)); + + Assert.assertEquals(Arrays.asList("B"), + searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "B"), 1)); + + Assert.assertEquals(Arrays.asList("C", "F", "L"), + searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("A", "L"), 3)); + } + + @Test + public void testUnreachableBinaryTreeGoalReturnsFailure() { + // "B" is a child of "A" — searching from B cannot reach A in this tree. + // With no path even at large depth, DLS should report failure (empty list), not cutoff. + Assert.assertEquals(Collections.emptyList(), + searchForActions(ProblemFactory.getSimpleBinaryTreeProblem("B", "A"), 10)); + } +}