diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1d5880c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Kolby Nottingham + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/patches/minecraft-data+3.84.1.patch b/patches/minecraft-data+3.84.1.patch new file mode 100644 index 0000000..c88a9f2 --- /dev/null +++ b/patches/minecraft-data+3.84.1.patch @@ -0,0 +1,913 @@ +diff --git a/node_modules/minecraft-data/minecraft-data/data/pc/1.21.1/blocks.json b/node_modules/minecraft-data/minecraft-data/data/pc/1.21.1/blocks.json +index 9d8c578..91e1d77 100644 +--- a/node_modules/minecraft-data/minecraft-data/data/pc/1.21.1/blocks.json ++++ b/node_modules/minecraft-data/minecraft-data/data/pc/1.21.1/blocks.json +@@ -1062,7 +1062,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -1088,7 +1088,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -1114,7 +1114,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -1141,7 +1141,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -2905,7 +2905,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -2932,7 +2932,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -2959,7 +2959,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -4998,7 +4998,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -5024,7 +5024,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -5218,7 +5218,7 @@ + "resistance": 1200.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -5603,7 +5603,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -5629,7 +5629,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -5655,7 +5655,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -8242,7 +8242,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -8274,7 +8274,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -11717,7 +11717,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -11743,7 +11743,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -11915,7 +11915,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -30948,7 +30948,7 @@ + "resistance": 1200.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -30973,7 +30973,7 @@ + "resistance": 1200.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -30998,7 +30998,7 @@ + "resistance": 1200.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 10, + "filterLight": 15, +@@ -31023,7 +31023,7 @@ + "resistance": 1200.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34549,7 +34549,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34576,7 +34576,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34603,7 +34603,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34630,7 +34630,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34657,7 +34657,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34684,7 +34684,7 @@ + "resistance": 3.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34711,7 +34711,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34738,7 +34738,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34765,7 +34765,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34792,7 +34792,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34819,7 +34819,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34846,7 +34846,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34873,7 +34873,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34900,7 +34900,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34927,7 +34927,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34954,7 +34954,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -34981,7 +34981,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35008,7 +35008,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35035,7 +35035,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35100,7 +35100,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35165,7 +35165,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35230,7 +35230,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35295,7 +35295,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35338,7 +35338,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35381,7 +35381,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35424,7 +35424,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35467,7 +35467,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35494,7 +35494,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35521,7 +35521,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35548,7 +35548,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35575,7 +35575,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35602,7 +35602,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35629,7 +35629,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35656,7 +35656,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -35683,7 +35683,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35748,7 +35748,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35813,7 +35813,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35878,7 +35878,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35943,7 +35943,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -35986,7 +35986,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -36029,7 +36029,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -36072,7 +36072,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 0, +@@ -36115,7 +36115,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36182,7 +36182,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36249,7 +36249,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36316,7 +36316,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36383,7 +36383,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36450,7 +36450,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36517,7 +36517,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36584,7 +36584,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36651,7 +36651,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36714,7 +36714,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36777,7 +36777,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36840,7 +36840,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36903,7 +36903,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -36966,7 +36966,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37029,7 +37029,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37092,7 +37092,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37155,7 +37155,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37188,7 +37188,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37221,7 +37221,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37254,7 +37254,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37287,7 +37287,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37320,7 +37320,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37353,7 +37353,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37386,7 +37386,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -37419,7 +37419,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37457,7 +37457,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37495,7 +37495,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37533,7 +37533,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37571,7 +37571,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37609,7 +37609,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37647,7 +37647,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37685,7 +37685,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -37723,7 +37723,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": true, + "emitLight": 0, + "filterLight": 0, +@@ -39352,7 +39352,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -39379,7 +39379,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -39406,7 +39406,7 @@ + "resistance": 6.0, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, +@@ -39653,7 +39653,7 @@ + "resistance": 3.5, + "stackSize": 64, + "diggable": true, +- "material": "incorrect_for_wooden_tool", ++ "material": "mineable/pickaxe", + "transparent": false, + "emitLight": 0, + "filterLight": 15, diff --git a/requirements.txt b/requirements.txt index 9c3cae3..cc29f6b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,4 +2,4 @@ boto3==1.37.11 botocore==1.37.11 pandas==2.2.3 prettytable==3.16.0 -tqdm==4.62.3 \ No newline at end of file +tqdm==4.62.3 diff --git a/settings.js b/settings.js index 5108f3f..b782097 100644 --- a/settings.js +++ b/settings.js @@ -38,13 +38,13 @@ const settings = { "code_timeout_mins": -1, // minutes code is allowed to run. -1 for no timeout "relevant_docs_count": 5, // number of relevant code function docs to select for prompting. -1 for all - "max_messages": process.env.MAX_MESSAGES || 15, // max number of messages to keep in context - "num_examples": process.env.NUM_EXAMPLES || 2, // number of examples to give to the model + "max_messages": 15, // max number of messages to keep in context + "num_examples": 2, // number of examples to give to the model "max_commands": -1, // max number of commands that can be used in consecutive responses. -1 for no limit "verbose_commands": true, // show full command syntax "narrate_behavior": true, // chat simple automatic actions ('Picking up item!') "chat_bot_messages": true, // publicly chat messages to other bots - "log_all_prompts": process.env.LOG_ALL || true, // log all prompts to file + "log_all_prompts": false, // log ALL prompts to file } // these environment variables override certain settings @@ -63,4 +63,14 @@ if (process.env.INSECURE_CODING) { if (process.env.BLOCKED_ACTIONS) { settings.blocked_actions = JSON.parse(process.env.BLOCKED_ACTIONS); } +if (process.env.MAX_MESSAGES) { + settings.max_messages = process.env.MAX_MESSAGES; +} +if (process.env.NUM_EXAMPLES) { + settings.num_examples = process.env.NUM_EXAMPLES; +} +if (process.env.LOG_ALL) { + settings.log_all_prompts = process.env.LOG_ALL; +} + export default settings; diff --git a/src/agent/agent.js b/src/agent/agent.js index 602d5ea..34f5cda 100644 --- a/src/agent/agent.js +++ b/src/agent/agent.js @@ -14,7 +14,7 @@ import { handleTranslation, handleEnglishTranslation } from '../utils/translator import { addBrowserViewer } from './vision/browser_viewer.js'; import settings from '../../settings.js'; import { serverProxy } from './agent_proxy.js'; -import { Task } from './tasks.js'; +import { Task } from './tasks/tasks.js'; import { say } from './speak.js'; export class Agent { @@ -62,7 +62,6 @@ export class Agent { } else { taskStart = Date.now(); } - // incorporate new restart time into task this.task = new Task(this, task_path, task_id, taskStart); this.blocked_actions = settings.blocked_actions.concat(this.task.blocked_actions || []); blacklistCommands(this.blocked_actions); @@ -105,7 +104,9 @@ export class Agent { this.startEvents(); if (!load_mem) { - this.task.initBotTask(); + if (task_path !== null) { + this.task.initBotTask(); + } } await new Promise((resolve) => setTimeout(resolve, 10000)); @@ -153,7 +154,7 @@ export class Agent { } } - this.respondFunc = respondFunc; + this.respondFunc = respondFunc; this.bot.on('whisper', respondFunc); if (settings.profiles.length === 1) @@ -199,8 +200,8 @@ export class Agent { if (missingPlayers.length > 0) { console.log(`Missing players/bots: ${missingPlayers.join(', ')}`); this.cleanKill('Not all required players/bots are present in the world. Exiting.', 4); - } } + } requestInterrupt() { this.bot.interrupt_code = true; diff --git a/src/agent/commands/queries.js b/src/agent/commands/queries.js index 531232b..ef77ade 100644 --- a/src/agent/commands/queries.js +++ b/src/agent/commands/queries.js @@ -2,7 +2,7 @@ import * as world from '../library/world.js'; import * as mc from '../../utils/mcdata.js'; import { getCommandDocs } from './index.js'; import convoManager from '../conversation.js'; -import { checkLevelBlueprint, checkBlueprint } from '../task_types/construction_tasks.js'; +import { checkLevelBlueprint, checkBlueprint } from '../tasks/construction_tasks.js'; import { load } from 'cheerio'; const pad = (str) => { diff --git a/src/agent/task_types/construction_tasks.js b/src/agent/tasks/construction_tasks.js similarity index 99% rename from src/agent/task_types/construction_tasks.js rename to src/agent/tasks/construction_tasks.js index f224966..c9a4a4d 100644 --- a/src/agent/task_types/construction_tasks.js +++ b/src/agent/tasks/construction_tasks.js @@ -1054,6 +1054,7 @@ export function blueprintToTask(blueprint_data, num_agents) { } let give_agent = 0; + console.log("materials", blueprint_data.materials) for (const key of Object.keys(blueprint_data.materials)) { initialInventory[JSON.stringify(give_agent)][key] = blueprint_data.materials[key]; give_agent = (give_agent + 1) % num_agents; @@ -1063,7 +1064,7 @@ export function blueprintToTask(blueprint_data, num_agents) { type: "construction", goal: "Make a structure with the blueprint below", conversation: "Let's share materials and make a structure with the blueprint", - agent_count: 2, + agent_count: num_agents, blueprint: blueprint_data, initial_inventory: initialInventory, }; diff --git a/src/agent/task_types/cooking_tasks.js b/src/agent/tasks/cooking_tasks.js similarity index 100% rename from src/agent/task_types/cooking_tasks.js rename to src/agent/tasks/cooking_tasks.js diff --git a/src/agent/tasks.js b/src/agent/tasks/tasks.js similarity index 96% rename from src/agent/tasks.js rename to src/agent/tasks/tasks.js index ed3aa82..975f37e 100644 --- a/src/agent/tasks.js +++ b/src/agent/tasks/tasks.js @@ -1,10 +1,9 @@ import { readFileSync , writeFileSync, existsSync} from 'fs'; -import { executeCommand } from './commands/index.js'; -import { getPosition } from './library/world.js'; -import settings from '../../settings.js'; -import { Vec3 } from 'vec3'; -import { ConstructionTaskValidator, Blueprint } from './task_types/construction_tasks.js'; -import { CookingTaskInitiator } from './task_types/cooking_tasks.js'; +import { executeCommand } from '../commands/index.js'; +import { getPosition } from '../library/world.js'; +import settings from '../../../settings.js'; +import { ConstructionTaskValidator, Blueprint } from './construction_tasks.js'; +import { CookingTaskInitiator } from './cooking_tasks.js'; const PROGRESS_FILE = './hells_kitchen_progress.json'; @@ -237,27 +236,23 @@ export class Task { constructor(agent, task_path, task_id, taskStartTime = null) { this.agent = agent; this.data = null; - console.log("task start time", taskStartTime); if (taskStartTime !== null) this.taskStartTime = taskStartTime; else this.taskStartTime = Date.now(); - console.log(this.taskStartTime); this.validator = null; this.reset_function = null; this.blocked_actions = []; this.task_id = task_id; - console.log('Task ID:', task_id); - - // Reset hells_kitchen progress when a new task starts - if (task_id && task_id.endsWith('hells_kitchen')) { - hellsKitchenProgressManager.resetTask(task_id); - console.log('Reset Hells Kitchen progress for new task'); - } - if (task_path && task_id) { + console.log('Starting task', task_id); + if (task_id.endsWith('hells_kitchen')) { + // Reset hells_kitchen progress when a new task starts + hellsKitchenProgressManager.resetTask(task_id); + console.log('Reset Hells Kitchen progress for new task'); + } this.data = this.loadTask(task_path, task_id); this.task_type = this.data.type; if (this.task_type === 'construction' && this.data.blueprint) { @@ -292,6 +287,9 @@ export class Task { if (this.conversation) this.blocked_actions.push('!endConversation'); } + else { + console.log('No task.'); + } this.name = this.agent.name; this.available_agents = settings.profiles.map((p) => JSON.parse(readFileSync(p, 'utf8')).name); diff --git a/src/models/gpt.js b/src/models/gpt.js index 8540778..4f33f22 100644 --- a/src/models/gpt.js +++ b/src/models/gpt.js @@ -21,7 +21,7 @@ export class GPT { async sendRequest(turns, systemMessage, stop_seq='***') { let messages = [{'role': 'system', 'content': systemMessage}].concat(turns); - + messages = strictFormat(messages); const pack = { model: this.model_name || "gpt-3.5-turbo", messages, @@ -29,7 +29,6 @@ export class GPT { ...(this.params || {}) }; if (this.model_name.includes('o1')) { - pack.messages = strictFormat(messages); delete pack.stop; } diff --git a/src/models/openrouter.js b/src/models/openrouter.js index 8e4530a..5cbc090 100644 --- a/src/models/openrouter.js +++ b/src/models/openrouter.js @@ -52,6 +52,24 @@ export class OpenRouter { return res; } + async sendVisionRequest(messages, systemMessage, imageBuffer) { + const imageMessages = [...messages]; + imageMessages.push({ + role: "user", + content: [ + { type: "text", text: systemMessage }, + { + type: "image_url", + image_url: { + url: `data:image/jpeg;base64,${imageBuffer.toString('base64')}` + } + } + ] + }); + + return this.sendRequest(imageMessages, systemMessage); + } + async embed(text) { throw new Error('Embeddings are not supported by Openrouter.'); } diff --git a/scripts/analyse_results.py b/tasks/analyse_results.py similarity index 79% rename from scripts/analyse_results.py rename to tasks/analyse_results.py index c83a83a..1fe4285 100644 --- a/scripts/analyse_results.py +++ b/tasks/analyse_results.py @@ -8,6 +8,13 @@ import argparse from tqdm import tqdm import glob +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define output directory for analysis results +analysis_output_dir = os.path.join(project_root, "experiments", "analysis_results") +# Ensure the output directory exists +os.makedirs(analysis_output_dir, exist_ok=True) + def download_s3_folders(bucket_name, s3_prefix, local_base_dir): """ Downloads groups of folders from S3 based on the next level of prefixes. @@ -23,6 +30,10 @@ def download_s3_folders(bucket_name, s3_prefix, local_base_dir): s3_client = boto3.client('s3') downloaded_folders = [] + # Ensure local_base_dir is relative to project root if not absolute + if not os.path.isabs(local_base_dir): + local_base_dir = os.path.join(project_root, local_base_dir) + try: # List objects with the prefix, delimited by '/' to find sub-prefixes (folders) response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=s3_prefix, Delimiter='/') @@ -207,42 +218,61 @@ def aggregate_results(local_folders): } def get_immediate_subdirectories(a_dir): + # Ensure a_dir is relative to project root if not absolute + if not os.path.isabs(a_dir): + a_dir = os.path.join(project_root, a_dir) return [os.path.join(a_dir, name) for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name))] # --- Main Execution --- if __name__ == "__main__": - # 1. Download folders from AWS + # 1. Download folders from AWS or use local directory parser = argparse.ArgumentParser() parser.add_argument('--s3_download', action="store_true", help='Download folders from S3') parser.add_argument('--aws_bucket_name', default="mindcraft" , type=str, help='AWS bucket name') parser.add_argument('--s3_folder_prefix', default="", type=str, help='S3 folder prefix') - parser.add_argument('--local_download_dir', default="results/", type=str, help='Local download directory') + # Change default input dir to 'experiments' relative to project root + parser.add_argument('--local_download_dir', default="experiments", type=str, help='Local directory containing results (relative to project root)') args = parser.parse_args() AWS_BUCKET_NAME = args.aws_bucket_name S3_FOLDER_PREFIX = args.s3_folder_prefix - if args.local_download_dir != "": - LOCAL_DOWNLOAD_DIR = args.local_download_dir + f"/{S3_FOLDER_PREFIX.replace('/', '_')}" + + # Resolve local_download_dir relative to project root + local_download_dir_abs = args.local_download_dir + if not os.path.isabs(local_download_dir_abs): + local_download_dir_abs = os.path.join(project_root, local_download_dir_abs) + + # Construct LOCAL_DOWNLOAD_DIR based on the absolute path + if args.local_download_dir != "": # Original check seems redundant now, but kept logic + LOCAL_DOWNLOAD_DIR = local_download_dir_abs # Already includes prefix if s3_download + if args.s3_download and S3_FOLDER_PREFIX: # Append S3 prefix if downloading + LOCAL_DOWNLOAD_DIR = os.path.join(local_download_dir_abs, S3_FOLDER_PREFIX.replace('/', '_').rstrip('_')) else: - LOCAL_DOWNLOAD_DIR = args.local_download_dir + LOCAL_DOWNLOAD_DIR = local_download_dir_abs # Should not happen with default if (args.s3_download): - print(f"Downloading folders from s3://{args.aws_bucket_name}/{args.s3_folder_prefix} to {args.local_download_dir}...") - folders = download_s3_folders(args.aws_bucket_name, args.s3_folder_prefix, args.local_download_dir) + print(f"Downloading folders from s3://{AWS_BUCKET_NAME}/{S3_FOLDER_PREFIX} to {LOCAL_DOWNLOAD_DIR}...") + # Pass the absolute base path for downloads + folders = download_s3_folders(AWS_BUCKET_NAME, S3_FOLDER_PREFIX, local_download_dir_abs) else: - folders = get_immediate_subdirectories(args.local_download_dir) + folders = get_immediate_subdirectories(local_download_dir_abs) print(folders) + + if not folders: + print("No folders found or downloaded. Exiting.") + exit() + results = aggregate_results(folders) print(results) - # Save results to a file - os.makedirs(LOCAL_DOWNLOAD_DIR, exist_ok=True) - with open(LOCAL_DOWNLOAD_DIR + "/results.txt", "w") as file: + # Hardcode output path within experiments/analysis_results/ + results_file_path = os.path.join(analysis_output_dir, "analyse_results_output.txt") + with open(results_file_path, "w") as file: file.write("Results\n") for key, value in results.items(): file.write(f"{key}: {value}\n") - print("Results saved to results.txt") + print(f"Results saved to {results_file_path}") # if not downloaded_local_folders: # print("No folders downloaded. Exiting.") # exit() diff --git a/scripts/analyze_construction_tasks.py b/tasks/analyze_construction_tasks.py similarity index 78% rename from scripts/analyze_construction_tasks.py rename to tasks/analyze_construction_tasks.py index 4c1f94f..a7a89e4 100644 --- a/scripts/analyze_construction_tasks.py +++ b/tasks/analyze_construction_tasks.py @@ -3,6 +3,16 @@ import json from collections import defaultdict from prettytable import PrettyTable import re +import argparse +import pandas as pd +import glob + +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define output directory for analysis results +analysis_output_dir = os.path.join(project_root, "experiments", "analysis_results") +# Ensure the output directory exists +os.makedirs(analysis_output_dir, exist_ok=True) def extract_success_scores(folders, model_names): assert len(folders) == len(model_names), "Folders and model names lists must have the same length." @@ -173,7 +183,49 @@ def extract_success_scores(folders, model_names): display_table("Average Success Score by Room", avg_room_scores) display_table("Average Success Score by (Material, Room) Tuples", avg_material_room_scores, tuple_keys=True) -# Example usage -folders = ["experiments/gpt-4o_construction_tasks", "experiments/claude-3-5-sonnet-latest_construction_tasks"] -model_names = ["GPT-4o", "Claude 3.5 sonnet"] -extract_success_scores(folders, model_names) \ No newline at end of file +def analyze_construction_log(log_file): + # ... existing code ... + pass + +def main(): + parser = argparse.ArgumentParser(description='Analyze construction task logs.') + # Change default input dir to 'experiments' relative to project root + parser.add_argument('--log_dir', type=str, default='experiments', + help='Directory containing the log files (relative to project root)') + # Removed --output_file argument + # parser.add_argument('--output_file', type=str, default='construction_analysis_results.csv', + # help='Output CSV file name (relative to project root)') + args = parser.parse_args() + + # Resolve log_dir path relative to project root + log_dir_abs = args.log_dir + if not os.path.isabs(log_dir_abs): + log_dir_abs = os.path.join(project_root, log_dir_abs) + + # Hardcode output file path + output_file_abs = os.path.join(analysis_output_dir, "construction_analysis.csv") + + all_results = [] + # Use absolute log directory path + log_pattern = os.path.join(log_dir_abs, '*.json') + print(f"Searching for logs in: {log_pattern}") + log_files_found = glob.glob(log_pattern) + print(f"Found {len(log_files_found)} log files.") + + for log_file in log_files_found: + results = analyze_construction_log(log_file) + if results: + all_results.append(results) + + if all_results: + df = pd.DataFrame(all_results) + # Ensure the output directory exists (already done at top) + # os.makedirs(os.path.dirname(output_file_abs), exist_ok=True) + # Save to hardcoded absolute output file path + df.to_csv(output_file_abs, index=False) + print(f"Analysis complete. Results saved to {output_file_abs}") + else: + print("No results generated from log files.") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/scripts/analyze_cooking_tasks.py b/tasks/analyze_cooking_tasks.py similarity index 84% rename from scripts/analyze_cooking_tasks.py rename to tasks/analyze_cooking_tasks.py index 7575d3c..094c932 100644 --- a/scripts/analyze_cooking_tasks.py +++ b/tasks/analyze_cooking_tasks.py @@ -3,6 +3,16 @@ import json import re from collections import defaultdict from prettytable import PrettyTable +import pandas as pd +import glob +import argparse + +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define output directory for analysis results +analysis_output_dir = os.path.join(project_root, "experiments", "analysis_results") +# Ensure the output directory exists +os.makedirs(analysis_output_dir, exist_ok=True) def extract_cooking_items(exp_dir): """Extract cooking items from experiment directory name.""" @@ -359,66 +369,52 @@ def generate_item_blocked_data(experiments_root): return item_blocked_data, ignored_tasks +def analyze_cooking_log(log_file): + # Placeholder for the actual analysis logic if it exists + # This function needs to be implemented based on the script's purpose + print(f"Analyzing {log_file}...") # Example print + # Example: return a dictionary of results + return {"file": os.path.basename(log_file), "score": 1} # Dummy result + def main(): - # Define lists for model directories and corresponding model names - model_dirs = [ - "experiments/gpt-4o_2agent_NEW_cooking_tasks", - # "experiments/claude-3-5-sonnet_2agent_NEW_cooking_tasks", - # "experiments/claude-3-5-sonnet_3agent_NEW_cooking_tasks", - "experiments/gpt-4o_3agent_NEW_cooking_tasks", - # "experiments/1_claude-3-5-sonnet_4agents_NEW_cooking_tasks", - "experiments/gpt-4o_4agents_NEW_cooking_tasks", - "experiments/gpt-4o_5agents_NEW_cooking_tasks", - # "experiments/" - ] - model_names = [ - "GPT-4o-2agent", - # "Claude-3.5-2agent", - "GPT-4o-3agent", - # "Claude-3.5-3agent", - # "Claude-3.5-4agent", - "GPT-4o-4agent", - "GPT-4o-5agent", - # "Another-Model" - ] + parser = argparse.ArgumentParser(description='Analyze cooking task logs.') + # Change default input dir to 'experiments' relative to project root + parser.add_argument('--log_dir', type=str, default='experiments', + help='Directory containing the log files (relative to project root)') + # Removed --output_file argument + # parser.add_argument('--output_file', type=str, default='cooking_analysis_results.csv', + # help='Output CSV file name (relative to project root)') + args = parser.parse_args() - # Ensure both lists are of the same size - if len(model_dirs) != len(model_names): - print("Error: The number of model directories and model names must be the same.") - return + # Resolve log_dir path relative to project root + log_dir_abs = args.log_dir + if not os.path.isabs(log_dir_abs): + log_dir_abs = os.path.join(project_root, log_dir_abs) + + # Hardcode output file path + output_file_abs = os.path.join(analysis_output_dir, "cooking_analysis.csv") - # Analyze each model directory - models_blocked_results = {} - models_item_results = {} - all_cooking_items = set() - total_ignored_tasks = 0 - - for model_dir, model_name in zip(model_dirs, model_names): - print(f"Analyzing {model_name} experiments in: {model_dir}") - - blocked_results, item_results, unique_items, ignored_tasks = analyze_experiments(model_dir, model_name) - - models_blocked_results[model_name] = blocked_results - models_item_results[model_name] = item_results - all_cooking_items.update(unique_items) - total_ignored_tasks += len(ignored_tasks) - - if ignored_tasks: - print(f" - {model_name}: Ignored {len(ignored_tasks)} tasks with no score information.") - - # Print summary of ignored tasks - if total_ignored_tasks > 0: - print(f"\nTotal ignored tasks (missing score information): {total_ignored_tasks}") - - # Print the comparison tables - print_model_comparison_blocked(models_blocked_results) - print_model_comparison_items(models_item_results, all_cooking_items) - - # Print overall statistics - print("\nUnique Cooking Items Found:") - print("=" * 60) - print(", ".join(sorted(all_cooking_items))) - print(f"Total unique items: {len(all_cooking_items)}") + all_results = [] + # Use absolute log directory path + log_pattern = os.path.join(log_dir_abs, '*.json') + print(f"Searching for logs in: {log_pattern}") + log_files_found = glob.glob(log_pattern) + print(f"Found {len(log_files_found)} log files.") + + for log_file in log_files_found: + results = analyze_cooking_log(log_file) + if results: + all_results.append(results) # Append the results dictionary + + if all_results: + df = pd.DataFrame(all_results) + # Ensure the output directory exists + os.makedirs(os.path.dirname(output_file_abs), exist_ok=True) + # Save to hardcoded absolute output file path + df.to_csv(output_file_abs, index=False) + print(f"Analysis complete. Results saved to {output_file_abs}") + else: + print("No results generated from log files.") if __name__ == "__main__": main() \ No newline at end of file diff --git a/scripts/analyze_crafting_tasks.py b/tasks/analyze_crafting_tasks.py similarity index 76% rename from scripts/analyze_crafting_tasks.py rename to tasks/analyze_crafting_tasks.py index 91e24c2..60485bd 100644 --- a/scripts/analyze_crafting_tasks.py +++ b/tasks/analyze_crafting_tasks.py @@ -8,6 +8,14 @@ import argparse from tqdm import tqdm import glob from prettytable import PrettyTable +import pandas as pd + +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define output directory for analysis results +analysis_output_dir = os.path.join(project_root, "experiments", "analysis_results") +# Ensure the output directory exists +os.makedirs(analysis_output_dir, exist_ok=True) def download_s3_folders(bucket_name, s3_prefix, local_base_dir): """ @@ -24,6 +32,10 @@ def download_s3_folders(bucket_name, s3_prefix, local_base_dir): s3_client = boto3.client('s3') downloaded_folders = [] + # Ensure local_base_dir is relative to project root if not absolute + if not os.path.isabs(local_base_dir): + local_base_dir = os.path.join(project_root, local_base_dir) + try: # List objects with the prefix, delimited by '/' to find sub-prefixes (folders) response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=s3_prefix, Delimiter='/') @@ -69,15 +81,16 @@ def analyze_json_file(file_path): file_path (str): Path to the JSON file. Returns: - str or None: The task outcome string if found, otherwise None. + bool: True if task was successful, False otherwise. """ try: with open(file_path, 'r') as f: data = json.load(f) if 'turns' in data and isinstance(data['turns'], list): - for turn in reversed(data['turns']): # Check turns from the end + for turn in data['turns']: # Check all turns, not just from the end if turn.get('role') == 'system' and isinstance(turn.get('content'), str): if "Task successful ended with code : 2" in turn['content'] or "Task ended with score : 1" in turn["content"] or "Task ended in score: 1" in turn["content"]: + # print(f"Success found in {file_path}") return True return False except FileNotFoundError: @@ -93,18 +106,17 @@ def analyze_json_file(file_path): def extract_result(folder_path): folder_name = os.path.basename(folder_path) json_files = glob.glob(os.path.join(folder_path, "*.json")) - assert len(json_files) == 2, f"Expected 2 json files in {folder_name}, found {len(json_files)}" - + if not json_files: print(f"No JSON files found in {folder_name}") return None else: - outcome = False + # Check each JSON file in the folder for success indication for json_file in json_files: outcome = analyze_json_file(json_file) - if outcome: + if outcome: # If any file indicates success, return True return True - return False + return False # Return False only if no files indicate success def is_base(folder_path): return "full_plan" in folder_path and "depth_0" in folder_path and "missing" not in folder_path @@ -240,6 +252,9 @@ def aggregate_results(local_folders): } def get_immediate_subdirectories(a_dir): + # Ensure a_dir is relative to project root if not absolute + if not os.path.isabs(a_dir): + a_dir = os.path.join(project_root, a_dir) return [os.path.join(a_dir, name) for name in os.listdir(a_dir) if os.path.isdir(os.path.join(a_dir, name))] @@ -285,30 +300,50 @@ def create_pretty_tables(results): return overall_table.get_string() + "\n\n" + depth_table.get_string() + "\n\n" + plan_table.get_string() -# --- Main Execution --- -if __name__ == "__main__": - # 1. Download folders from AWS +def analyze_crafting_log(log_file): + # ... existing code ... + pass + +def main(): + # 1. Download folders from AWS or use local directory parser = argparse.ArgumentParser() parser.add_argument('--s3_download', action="store_true", help='Download folders from S3') parser.add_argument('--aws_bucket_name', default="mindcraft" , type=str, help='AWS bucket name') parser.add_argument('--s3_folder_prefix', default="", type=str, help='S3 folder prefix') - parser.add_argument('--local_download_dir', default="results/", type=str, help='Local download directory') + # Change default input dir to 'experiments' relative to project root + parser.add_argument('--local_download_dir', default="experiments", type=str, help='Local directory containing results (relative to project root)') args = parser.parse_args() AWS_BUCKET_NAME = args.aws_bucket_name S3_FOLDER_PREFIX = args.s3_folder_prefix - if args.local_download_dir != "": - LOCAL_DOWNLOAD_DIR = args.local_download_dir + f"/{S3_FOLDER_PREFIX.replace('/', '_')}" + + # Resolve local_download_dir relative to project root + local_download_dir_abs = args.local_download_dir + if not os.path.isabs(local_download_dir_abs): + local_download_dir_abs = os.path.join(project_root, local_download_dir_abs) + + # Construct LOCAL_DOWNLOAD_DIR based on the absolute path + # This directory will be used for results aggregation and saving output files + if args.local_download_dir != "": + LOCAL_DOWNLOAD_DIR = local_download_dir_abs # Base results directory + if args.s3_download and S3_FOLDER_PREFIX: # Append S3 prefix if downloading to keep results separate + LOCAL_DOWNLOAD_DIR = os.path.join(local_download_dir_abs, S3_FOLDER_PREFIX.replace('/', '_').rstrip('_')) else: - LOCAL_DOWNLOAD_DIR = args.local_download_dir + LOCAL_DOWNLOAD_DIR = local_download_dir_abs # Should not happen with default if (args.s3_download): - print(f"Downloading folders from s3://{args.aws_bucket_name}/{args.s3_folder_prefix} to {args.local_download_dir}...") - folders = download_s3_folders(args.aws_bucket_name, args.s3_folder_prefix, args.local_download_dir) + print(f"Downloading folders from s3://{AWS_BUCKET_NAME}/{S3_FOLDER_PREFIX} to {LOCAL_DOWNLOAD_DIR}...") + # Pass the absolute base path for downloads, download_s3_folders handles subfolder creation + folders = download_s3_folders(AWS_BUCKET_NAME, S3_FOLDER_PREFIX, local_download_dir_abs) else: - folders = get_immediate_subdirectories(args.local_download_dir) - print(folders) + # Use the absolute path to get subdirectories + folders = get_immediate_subdirectories(local_download_dir_abs) + print(f"Found local folders: {folders}") + if not folders: + print("No folders found or downloaded. Exiting.") + exit() + results = aggregate_results(folders) print(results) @@ -316,17 +351,29 @@ if __name__ == "__main__": tables_output = create_pretty_tables(results) print("\n" + tables_output) - # Save results to files - os.makedirs(LOCAL_DOWNLOAD_DIR, exist_ok=True) + # Save results to files within the hardcoded experiments/analysis_results/ directory + # os.makedirs(LOCAL_DOWNLOAD_DIR, exist_ok=True) # Output dir created at top # Save raw results - with open(LOCAL_DOWNLOAD_DIR + "/results.txt", "w") as file: + # Determine filename based on S3 prefix or local dir name if possible + if S3_FOLDER_PREFIX: + results_filename_base = S3_FOLDER_PREFIX.replace('/', '_').rstrip('_') + else: + results_filename_base = os.path.basename(local_download_dir_abs) if local_download_dir_abs else "local" + results_filename_base = f"crafting_analysis_{results_filename_base}" + + results_file_path = os.path.join(analysis_output_dir, f"{results_filename_base}_results.txt") + with open(results_file_path, "w") as file: file.write("Results\n") for key, value in results.items(): file.write(f"{key}: {value}\n") # Save pretty tables - with open(LOCAL_DOWNLOAD_DIR + "/results_tables.txt", "w") as file: + tables_file_path = os.path.join(analysis_output_dir, f"{results_filename_base}_tables.txt") + with open(tables_file_path, "w") as file: file.write(tables_output) - print("Results saved to results.txt and tables saved to results_tables.txt") \ No newline at end of file + print(f"Results saved to {results_file_path} and tables saved to {tables_file_path}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/tasks/construction_tasks/README_ConstructionTasks.md b/tasks/construction_tasks/README_ConstructionTasks.md index 352127e..51aa19a 100644 --- a/tasks/construction_tasks/README_ConstructionTasks.md +++ b/tasks/construction_tasks/README_ConstructionTasks.md @@ -31,4 +31,4 @@ The generation code is documented to help with customization. - `profiles/task_construct.json` - Default configuration profile - `tasks/construction_tasks/test_multiagent_construction_tasks.json` - Training task definitions (initalized with 5 variants) - `tasks/construction_tasks/test_multiagent_construction_tasks.json` - Test task definitions (initalized with 1 variant) -- `src/agent/task_types/construction_tasks.js` - Blueprint Class, Construction Validation Class, and Procedural Generation Function \ No newline at end of file +- `src/agent/tasks/construction_tasks.js` - Blueprint Class, Construction Validation Class, and Procedural Generation Function \ No newline at end of file diff --git a/tasks/construction_tasks/custom/church_three_agents.json b/tasks/construction_tasks/custom/church_three_agents.json new file mode 100644 index 0000000..af4219f --- /dev/null +++ b/tasks/construction_tasks/custom/church_three_agents.json @@ -0,0 +1,2357 @@ +{ + "church_three_agents": { + "type": "construction", + "goal": "Make a structure with the blueprint below", + "conversation": "Let's share materials and make a structure with the blueprint", + "agent_count": 3, + "blueprint": { + "materials": { + "oak_planks": 153, + "stone_bricks": 142, + "oak_door": 2, + "oak_stairs": 16, + "quartz_block": 1, + "glass_pane": 15, + "torch": 4, + "oak_fence": 4 + }, + "levels": [ + { + "level": 0, + "coordinates": [ + -18, + -60, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 1, + "coordinates": [ + -18, + -59, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "stone_bricks", + "stone_bricks", + "oak_door", + "stone_bricks", + "air", + "air", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "oak_stairs", + "oak_stairs", + "air", + "oak_stairs", + "oak_stairs", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "oak_stairs", + "oak_stairs", + "air", + "oak_stairs", + "oak_stairs", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "oak_stairs", + "oak_stairs", + "air", + "oak_stairs", + "oak_stairs", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "oak_stairs", + "oak_stairs", + "air", + "oak_stairs", + "oak_stairs", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "quartz_block", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 2, + "coordinates": [ + -18, + -58, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "glass_pane", + "stone_bricks", + "oak_door", + "stone_bricks", + "glass_pane", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "glass_pane", + "air", + "air", + "air", + "air", + "air", + "glass_pane", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "glass_pane", + "air", + "air", + "air", + "air", + "air", + "glass_pane", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "glass_pane", + "air", + "air", + "air", + "air", + "air", + "glass_pane", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "glass_pane", + "air", + "air", + "air", + "air", + "air", + "glass_pane", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "glass_pane", + "air", + "air", + "air", + "air", + "air", + "glass_pane", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "glass_pane", + "stone_bricks", + "glass_pane", + "stone_bricks", + "glass_pane", + "stone_bricks", + "air", + "air", + "air" + ] + ] + }, + { + "level": 3, + "coordinates": [ + -18, + -57, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "torch", + "air", + "air", + "air", + "torch", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "torch", + "air", + "air", + "air", + "torch", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ] + ] + }, + { + "level": 4, + "coordinates": [ + -18, + -56, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ] + ] + }, + { + "level": 5, + "coordinates": [ + -18, + -55, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air" + ], + [ + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "stone_bricks", + "air", + "air", + "air" + ] + ] + }, + { + "level": 6, + "coordinates": [ + -18, + -54, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ], + [ + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "oak_planks", + "air", + "air", + "air" + ] + ] + }, + { + "level": 7, + "coordinates": [ + -18, + -53, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 8, + "coordinates": [ + -18, + -52, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 9, + "coordinates": [ + -18, + -51, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "stone_bricks", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 10, + "coordinates": [ + -18, + -50, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "oak_fence", + "oak_fence", + "oak_fence", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 11, + "coordinates": [ + -18, + -49, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "oak_fence", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 12, + "coordinates": [ + -18, + -48, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 13, + "coordinates": [ + -18, + -47, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 14, + "coordinates": [ + -18, + -46, + 29 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + } + ] + }, + "initial_inventory": { + "0": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "oak_planks": 153, + "oak_stairs": 16, + "torch": 4 + }, + "1": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "stone_bricks": 142, + "quartz_block": 1, + "oak_fence": 4 + }, + "2": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "oak_door": 2, + "glass_pane": 15 + } + } + } +} \ No newline at end of file diff --git a/tasks/construction_tasks/custom/flower_three_agents.json b/tasks/construction_tasks/custom/flower_three_agents.json new file mode 100644 index 0000000..7d1a11a --- /dev/null +++ b/tasks/construction_tasks/custom/flower_three_agents.json @@ -0,0 +1,1351 @@ +{ + "flower_three_agents": { + "type": "construction", + "goal": "Make a structure with the blueprint below", + "conversation": "Let's share materials and make a structure with the blueprint", + "agent_count": 3, + "blueprint": { + "materials": { + "yellow_terracotta": 94, + "brown_wool": 10, + "black_wool": 60, + "green_wool": 10, + "orange_wool": 58, + "red_wool": 44, + "yellow_wool": 49, + "magenta_wool": 66, + "purple_wool": 73, + "blue_wool": 87 + }, + "levels": [ + { + "level": 0, + "coordinates": [ + -124, + -60, + 133 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta" + ], + [ + "air", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta" + ], + [ + "air", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool", + "yellow_terracotta", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool", + "yellow_terracotta", + "yellow_terracotta", + "brown_wool" + ], + [ + "air", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "yellow_terracotta", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "yellow_terracotta" + ], + [ + "air", + "green_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "green_wool", + "green_wool", + "green_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "green_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool" + ], + [ + "air", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "green_wool", + "green_wool", + "green_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool", + "green_wool", + "green_wool", + "black_wool", + "black_wool", + "black_wool", + "black_wool" + ], + [ + "air", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool" + ], + [ + "air", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool", + "orange_wool" + ], + [ + "air", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool" + ], + [ + "air", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool", + "red_wool" + ], + [ + "air", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool" + ], + [ + "air", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool" + ], + [ + "air", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool", + "magenta_wool" + ], + [ + "air", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool" + ], + [ + "air", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "yellow_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool" + ], + [ + "air", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool", + "purple_wool" + ], + [ + "air", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool" + ], + [ + "air", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool" + ], + [ + "air", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool", + "blue_wool" + ] + ] + }, + { + "level": 1, + "coordinates": [ + -124, + -59, + 133 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + } + ] + }, + "initial_inventory": { + "0": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "yellow_terracotta": 94, + "green_wool": 10, + "yellow_wool": 49, + "blue_wool": 87 + }, + "1": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "brown_wool": 10, + "orange_wool": 58, + "magenta_wool": 66 + }, + "2": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "black_wool": 60, + "red_wool": 44, + "purple_wool": 73 + } + } + } +} \ No newline at end of file diff --git a/tasks/construction_tasks/custom/pyramid_three_agents.json b/tasks/construction_tasks/custom/pyramid_three_agents.json new file mode 100644 index 0000000..a3dacd3 --- /dev/null +++ b/tasks/construction_tasks/custom/pyramid_three_agents.json @@ -0,0 +1,699 @@ +{ + "pyramid_three_agents": { + "type": "construction", + "goal": "Make a structure with the blueprint below", + "conversation": "Let's share materials and make a structure with the blueprint", + "agent_count": 3, + "blueprint": { + "materials": { + "polished_granite": 1, + "gold_block": 27, + "stone_bricks": 41, + "polished_andesite": 34, + "quartz_block": 16, + "stone": 23, + "polished_diorite": 21, + "quartz_pillar": 2, + "glowstone": 3 + }, + "levels": [ + { + "level": 0, + "coordinates": [ + -60, + -60, + 6 + ], + "placement": [ + [ + "polished_granite", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "gold_block", + "stone_bricks", + "polished_andesite", + "gold_block", + "quartz_block", + "polished_andesite", + "gold_block", + "stone_bricks", + "gold_block" + ], + [ + "air", + "stone_bricks", + "polished_andesite", + "stone", + "polished_diorite", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "gold_block" + ], + [ + "air", + "polished_andesite", + "stone", + "polished_diorite", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks" + ], + [ + "air", + "gold_block", + "polished_diorite", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite" + ], + [ + "air", + "quartz_block", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite", + "quartz_block" + ], + [ + "air", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite", + "polished_diorite", + "stone_bricks" + ], + [ + "air", + "gold_block", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite", + "polished_diorite", + "stone_bricks", + "polished_andesite" + ], + [ + "air", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite", + "polished_diorite", + "stone_bricks", + "polished_andesite", + "gold_block" + ], + [ + "air", + "gold_block", + "gold_block", + "stone_bricks", + "polished_andesite", + "quartz_block", + "stone_bricks", + "polished_andesite", + "gold_block", + "gold_block" + ] + ] + }, + { + "level": 1, + "coordinates": [ + -60, + -59, + 6 + ], + "placement": [ + [ + "quartz_pillar", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "gold_block", + "stone_bricks", + "polished_andesite", + "quartz_block", + "stone_bricks", + "polished_andesite", + "gold_block", + "air" + ], + [ + "air", + "air", + "stone_bricks", + "stone", + "polished_diorite", + "polished_andesite", + "stone", + "stone_bricks", + "stone_bricks", + "air" + ], + [ + "air", + "air", + "polished_andesite", + "polished_diorite", + "polished_andesite", + "stone", + "stone_bricks", + "polished_diorite", + "polished_andesite", + "air" + ], + [ + "air", + "air", + "quartz_block", + "polished_andesite", + "stone", + "glowstone", + "polished_diorite", + "stone", + "quartz_block", + "air" + ], + [ + "air", + "air", + "stone_bricks", + "stone", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "stone_bricks", + "air" + ], + [ + "air", + "air", + "polished_andesite", + "stone_bricks", + "polished_diorite", + "stone", + "stone_bricks", + "polished_andesite", + "polished_andesite", + "air" + ], + [ + "air", + "air", + "gold_block", + "stone_bricks", + "polished_andesite", + "quartz_block", + "stone_bricks", + "polished_andesite", + "gold_block", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 2, + "coordinates": [ + -60, + -58, + 6 + ], + "placement": [ + [ + "quartz_pillar", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "gold_block", + "stone_bricks", + "quartz_block", + "gold_block", + "gold_block", + "air", + "air" + ], + [ + "air", + "air", + "air", + "stone_bricks", + "polished_diorite", + "polished_andesite", + "stone", + "polished_andesite", + "air", + "air" + ], + [ + "air", + "air", + "air", + "quartz_block", + "polished_andesite", + "glowstone", + "stone_bricks", + "quartz_block", + "air", + "air" + ], + [ + "air", + "air", + "air", + "gold_block", + "stone", + "stone_bricks", + "polished_diorite", + "stone_bricks", + "air", + "air" + ], + [ + "air", + "air", + "air", + "gold_block", + "polished_andesite", + "quartz_block", + "stone_bricks", + "gold_block", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 3, + "coordinates": [ + -60, + -57, + 6 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "gold_block", + "quartz_block", + "gold_block", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "quartz_block", + "glowstone", + "quartz_block", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "gold_block", + "quartz_block", + "gold_block", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + }, + { + "level": 4, + "coordinates": [ + -60, + -56, + 6 + ], + "placement": [ + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "gold_block", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ], + [ + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air", + "air" + ] + ] + } + ] + }, + "initial_inventory": { + "0": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "polished_granite": 1, + "polished_andesite": 34, + "polished_diorite": 21 + }, + "1": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "gold_block": 27, + "quartz_block": 16, + "quartz_pillar": 2 + }, + "2": { + "diamond_pickaxe": 1, + "diamond_axe": 1, + "diamond_shovel": 1, + "stone_bricks": 41, + "stone": 23, + "glowstone": 3 + } + } + } +} \ No newline at end of file diff --git a/tasks/construction_tasks/generate_multiagent_construction_tasks.js b/tasks/construction_tasks/generate_multiagent_construction_tasks.js index efd3d54..161981b 100644 --- a/tasks/construction_tasks/generate_multiagent_construction_tasks.js +++ b/tasks/construction_tasks/generate_multiagent_construction_tasks.js @@ -1,6 +1,6 @@ import fs from 'fs'; import path from 'path'; -import {proceduralGeneration} from "../../src/agent/task_types/construction_tasks.js"; +import {proceduralGeneration} from "../../src/agent/tasks/construction_tasks.js"; //note 'main' (script to run generation of tasks) is at bottom of page diff --git a/tasks/construction_tasks/get_blueprint.js b/tasks/construction_tasks/get_blueprint.js index 2fda3c7..dcf2493 100644 --- a/tasks/construction_tasks/get_blueprint.js +++ b/tasks/construction_tasks/get_blueprint.js @@ -1,5 +1,5 @@ import mineflayer from 'mineflayer'; -import { worldToBlueprint, blueprintToTask } from '../../src/agent/task_types/construction_tasks.js'; +import { worldToBlueprint, blueprintToTask } from '../../src/agent/tasks/construction_tasks.js'; import fs from 'fs'; import { start } from 'repl'; @@ -14,32 +14,33 @@ bot.on('spawn', async () => { console.log("Bot spawned. Starting blueprint check..."); // set this to be minX, minY, minZ const startCoord = { - x: -60, + x: -124, y: 1, - z: 6, + z: 133, } bot.chat(`/tp andy ${startCoord.x} ${startCoord.y} ${startCoord.z}`); - const yOffset = 5; - const xOffset = 10; - const zOffset = 10; + const yOffset = 2; + const xOffset = 30; + const zOffset = 20; - const taskFilePath = '/Users/isadorawhite/izzy_mindcraft/mindcraft/tasks/construction_tasks/custom/pyramid.json'; - const task_name = "pyramid"; + const taskFilePath = '/Users/isadorawhite/izzy_mindcraft/mindcraft/tasks/construction_tasks/custom/flower_three_agents.json'; + const task_name = "flower_three_agents"; setTimeout(async () => { let task_blueprint = await worldToBlueprint(startCoord, yOffset, xOffset, zOffset, bot); - for (const level of task_blueprint.levels) { + for (let i = 0; i < task_blueprint.levels.length; i++) { // Perform operations on each level + const level = task_blueprint.levels[i]; console.log("Level coordinates:", level.coordinates); - const new_coordinates = [level.coordinates[0], -60, level.coordinates[2]]; + const new_coordinates = [level.coordinates[0], -60 + i, level.coordinates[2]]; level.coordinates = new_coordinates; console.log("New coordinates:", level.coordinates); } console.log("Blueprint generated:", task_blueprint.levels[0].coordinates); - const task = blueprintToTask(task_blueprint, 2); + const task = blueprintToTask(task_blueprint, 3); const task_collection = {} task_collection[task_name] = task; diff --git a/evaluation_script.py b/tasks/evaluation_script.py similarity index 60% rename from evaluation_script.py rename to tasks/evaluation_script.py index 185d5ff..22523f9 100644 --- a/evaluation_script.py +++ b/tasks/evaluation_script.py @@ -16,6 +16,11 @@ import socket from tqdm import tqdm import boto3 +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define tasks directory +tasks_dir = os.path.dirname(os.path.abspath(__file__)) + BLOCKED_ACTIONS_COOKING = [ '!activate', '!attackPlayer', '!checkBlueprint', '!checkBlueprintLevel', '!clearChat', '!clearFurnace', '!consume', '!craftable', '!discard', @@ -57,7 +62,6 @@ def analyze_json_file(file_path): for turn in data["turns"]: if turn.get("role") == "system" and "content" in turn: if isinstance(turn["content"], str) and "Task ended with score : " in turn["content"]: - score_found = True if "Task ended with score : 1" in turn["content"]: return 1 elif "Task ended with score : 0" in turn["content"]: @@ -66,7 +70,8 @@ def analyze_json_file(file_path): score = float(turn["content"].split(":")[-1].strip()) return score - return False + + return None except FileNotFoundError: print(f"Error: File not found: {file_path}") return None @@ -86,11 +91,14 @@ def extract_result(folder_path): return None else: score = None + curr_score = 0 for json_file in json_files: score = analyze_json_file(json_file) if score is not None: - return score - return 0 + max_score = max(score, curr_score) + curr_score = max_score + + return curr_score def aggregate_results(local_folders): """ @@ -106,24 +114,98 @@ def aggregate_results(local_folders): total = 0 successful = 0 + successful_tasks = [] + + task_type = local_folders[0].split("/")[-2] + if "cooking" in task_type: + task_type = "cooking" + elif "techtree" in task_type: + task_type = "techtree" + elif "construction" in task_type: + task_type = "construction" + for folder_path in tqdm(local_folders): folder_name = os.path.basename(folder_path) try: result = extract_result(folder_path) + + if result == 1: + successful_tasks.append(folder_name) if result is not None: total += 1 successful += result except Exception as e: print(f"Error processing {folder_name}: {e}") + + successful_tasks.sort() + + if task_type == "construction": + successful = successful / total return { "total": total, "successful": successful, } +def check_folder_results(folder_path): + """ + Evaluate all JSON files in a folder and its subfolders and calculate success metrics. + + Args: + folder_path (str): Path to the folder containing JSON log files. + + Returns: + dict: A dictionary with success metrics. + """ + print(f"Checking results in folder: {folder_path}") + + # Check if the folder exists + if not os.path.exists(folder_path): + print(f"Error: Folder not found: {folder_path}") + return None + + # Find all subfolders (task IDs) in the given folder + if os.path.isdir(folder_path): + subfolders = [f for f in glob.glob(os.path.join(folder_path, "*")) if os.path.isdir(f)] + if subfolders: + # If there are subfolders, evaluate each subfolder + print(f"Found {len(subfolders)} subfolders to evaluate") + results = aggregate_results(subfolders) + else: + # If no subfolders, treat the folder itself as a results folder + print("No subfolders found, evaluating the folder itself") + results = aggregate_results([folder_path]) + + # Calculate success rate + if results["total"] > 0: + results["success_rate"] = results["successful"] / results["total"] + else: + results["success_rate"] = 0.0 + + # Print summary + print("\n=== Evaluation Results ===") + print(f"Total tasks evaluated: {results['total']}") + + if "construction" not in folder_path: + print(f"Successful tasks: {results['successful']}") + + if "construction" not in folder_path: + print(f"Success rate: {results['success_rate']:.2f}") + else: + print(f"Success rate: {results['successful']:.2f}") + + return results + else: + print(f"Error: {folder_path} is not a directory") + return None + def read_settings(file_path): """Read and parse the settings.js file to get agent profiles.""" + # Ensure file_path is absolute or relative to project_root + if not os.path.isabs(file_path): + file_path = os.path.join(project_root, file_path) + with open(file_path, 'r', encoding='utf-8') as file: content = file.read() @@ -151,7 +233,10 @@ def read_settings(file_path): def update_keys_json(): """Update the keys.json file with the specified key-value pair.""" - with open("keys.example.json", 'r', encoding='utf-8') as file: + keys_example_path = os.path.join(project_root, "keys.example.json") + keys_path = os.path.join(project_root, "keys.json") + + with open(keys_example_path, 'r', encoding='utf-8') as file: content = file.read() data = json.loads(content) @@ -161,7 +246,7 @@ def update_keys_json(): if env_value: # If the variable exists, update it data[key] = env_value - with open("keys.json", 'w', encoding='utf-8') as file: + with open(keys_path, 'w', encoding='utf-8') as file: json.dump(data, file, indent=4) def set_environment_variable_tmux_session(session_name, key, value): @@ -186,6 +271,14 @@ def launch_parallel_experiments(task_path, block_conversation=False, run_in_tmux=True): + # Resolve relative template_profile path + if not os.path.isabs(template_profile): + template_profile = os.path.join(project_root, template_profile) + + # Resolve relative task_path path + if not os.path.isabs(task_path): + task_path = os.path.join(project_root, task_path) + with open(task_path, 'r', encoding='utf-8') as file: content = file.read() json_data = json.loads(content) @@ -294,19 +387,16 @@ def launch_server_experiment(task_path, block_conversation=False, run_in_tmux=True): - """ - Launch a Minecraft server and run experiments on it. - @param task_path: Path to the task file - @param task_ids: IDs of the tasks to run - @param num_exp: Number of experiments to run - @param server: Tuple containing server path and port - @param experiments_folder: Folder to store experiment results - @param exp_name: Name of the experiment for wandb dataset - @param num_agents: Number of agents to run - @param model: Model to use for the agents - @param s3: Boolean flag to enable S3 upload - @param bucket_name: Name of the S3 bucket - """ + # Resolve relative template_profile path + if not os.path.isabs(template_profile): + template_profile = os.path.join(project_root, template_profile) + + # Resolve relative task_path path + if not os.path.isabs(task_path): + task_path = os.path.join(project_root, task_path) + + experiments_folder = os.path.join(project_root, experiments_folder) + server_path, server_port = server edit_file(os.path.join(server_path, "server.properties"), {"server-port": server_port}) mindserver_port = server_port - 55916 + 8080 @@ -446,55 +536,66 @@ def run_script(task_path, s3_path="mindcraft-experiments", session_name="0", run_in_tmux=True,): - script_content = "" - for task_id in task_ids: - # Create a separate folder for each task_id - task_folder = os.path.join(experiments_folder, str(task_id)) - os.makedirs(task_folder, exist_ok=True) - assert os.path.exists(task_folder), f"Directory {task_folder} was not created" - print(f"Created directory: {task_folder}") + + # Resolve relative task_path path + if not os.path.isabs(task_path): + task_path = os.path.join(project_root, task_path) + + # Resolve relative experiments_folder path + if not os.path.isabs(experiments_folder): + experiments_folder = os.path.join(project_root, experiments_folder) + + # Resolve relative server_path path + if not os.path.isabs(server_path): + server_path = os.path.join(project_root, server_path) - cmd = f"node main.js --task_path \'{task_path}\' --task_id {task_id}" - cp_cmd = f"cp {agent_names[0]}.json {server_path}bots/{agent_names[0]}/profile.json" - for _ in range(num_exp): - script_content += f"{cmd}\n" - script_content += "sleep 2\n" - for agent in agent_names: - agent_file_path = os.path.join(task_folder, f"{agent}_{_}.json") - script_content += f"echo 'Saving to {agent_file_path}'\n" - cp_cmd = f"cp bots/{agent}/memory.json {agent_file_path}" - script_content += f"echo '{cp_cmd}'\n" - script_content += f"{cp_cmd}\n" - script_content += "sleep 1\n" - if s3: - s3_cmd = f"aws s3 cp {agent_file_path} s3://{s3_path}/{task_id}/{agent}_{_}.json" - script_content += f"echo 'Uploading {agent_file_path} to S3'\n" - script_content += f"echo '{s3_cmd}'\n" - script_content += f"{s3_cmd}\n" - script_content += "sleep 1\n" - script_content += f"sleep 10\n" - if s3: - for agent in agent_names: - script_content += f"aws s3 cp bots/{agent} s3://{s3_path}/bots/{agent} --recursive\n" - - # Create a temporary shell script file - script_file = f"./tmp/experiment_script_{session_name}.sh" - make_script_file_and_run(script_content, script_file, session_name=session_name, run_in_tmux=run_in_tmux) + # Construct command (assuming main.js is in root) + main_js_path = os.path.join(project_root, "main.js") + + for exp in range(num_exp): + for task_id in task_ids: + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + exp_folder = os.path.join(experiments_folder, f"{task_id}_{exp}_{timestamp}") + + # Need to create the folder first if using subprocess and cwd + os.makedirs(exp_folder, exist_ok=True) + cmd = [ + "node", main_js_path, + "--task_path", task_path, + "--task_id", task_id, + "--agent_name", agent_names[0], + "--agent_name", agent_names[1], + "--server", server_path, + "--logs_path", exp_folder, # Ensure logs_path is absolute or handled by main.js relative to root + ] + + if s3: + cmd.extend(["--s3", "--s3_path", s3_path]) + + script_content = " ".join(cmd) + make_script_file_and_run(script_content, file_name=f"exp_{exp}_{task_id}_{timestamp}.sh", session_name=session_name, run_in_tmux=run_in_tmux) + + print(f"Launched Experiment {exp+1}/{num_exp} for Task {task_id}") + time.sleep(1) # Stagger launches def make_ops(agent_names, session_name): """Make the agents operators in the Minecraft world.""" print('Making agents operators...') - cmd = f"node main.js --task_path tasks/example_tasks.json --task_id debug_{len(agent_names)}_agent_timeout" + # Construct path to example tasks relative to project_root + example_task_path = os.path.join(project_root, "tasks/example_tasks.json") + cmd = f"node {os.path.join(project_root, 'main.js')} --task_path {example_task_path} --task_id debug_{len(agent_names)}_agent_timeout" - subprocess.run(["tmux", "send-keys", "-t", session_name, cmd, "C-m"]) + subprocess.run(["tmux", "send-keys", "-t", session_name, cmd, "C-m"], cwd=project_root) time.sleep(30) subprocess.run(["tmux", "send-keys", "-t", "server_" + session_name, f"/op @a", "C-m"]) - agents_op = check_agent_ops(agent_names, ops_file=f"./server_data_{session_name}/ops.json") + # Check ops file inside the correct tasks/server_data/X directory + ops_file_path = os.path.join(tasks_dir, "server_data", session_name, "ops.json") + agents_op = check_agent_ops(agent_names, ops_file=ops_file_path) if agents_op: print("Agents are operators! You are good to go :D") else: @@ -502,6 +603,15 @@ def make_ops(agent_names, session_name): make_ops(agent_names, session_name) def check_agent_ops(agent_names, ops_file="ops.json"): + """Check if agents are OPs on the server.""" + # ops_file path is now provided absolute by caller (make_ops) + # if not os.path.isabs(ops_file): + # ops_file = os.path.join(project_root, ops_file) # OLD LOGIC + + if not os.path.exists(ops_file): + print(f"Error: ops.json file not found: {ops_file}") + return False + with open(ops_file, "r") as f: ops_data = json.load(f) @@ -516,26 +626,39 @@ def make_script_file_and_run(script_content, file_name, session_name="0", run_in_tmux=True): - script_dir = os.path.dirname(file_name) - os.makedirs(script_dir, exist_ok=True) + # Create script inside tasks/tmp/ + script_base_dir = os.path.join(tasks_dir, "tmp") + os.makedirs(script_base_dir, exist_ok=True) + script_abs_path = os.path.join(script_base_dir, file_name) + + script_dir = os.path.dirname(script_abs_path) + # os.makedirs(script_dir, exist_ok=True) # Already handled by script_base_dir creation assert os.path.exists(script_dir), f"Script directory {script_dir} was not created" print(f"Created script directory: {script_dir}") # Call the function before writing the script file - with open(file_name, 'w') as f: + with open(script_abs_path, 'w') as f: f.write(script_content) - assert os.path.exists(file_name), f"Script file {file_name} was not created" + assert os.path.exists(script_abs_path), f"Script file {script_abs_path} was not created" - script_file_run = "bash " + file_name + script_file_run = "bash " + script_abs_path # Execute the shell script using subprocess + # Run subprocess from project_root so node main.js etc work if run_in_tmux: - subprocess.run(["tmux", "send-keys", "-t", session_name, script_file_run, "C-m"]) + subprocess.run(["tmux", "send-keys", "-t", session_name, script_file_run, "C-m"], cwd=project_root) else: - subprocess.run(script_file_run.split()) + subprocess.run(script_file_run.split(), cwd=project_root) def make_profiles(agent_names, models, apis, template_profile="profiles/collab_profile.json", url="http://127.0.0.1:8000/v1"): - assert len(agent_names) == len(models) + """Generate profile JSON files for each agent.""" + + # Resolve relative template_profile path relative to project_root + if template_profile.startswith("profiles/") and not os.path.isabs(template_profile): + template_profile = os.path.join(project_root, template_profile) + elif not os.path.isabs(template_profile): + # Assume relative to tasks dir if not in profiles/ structure + template_profile = os.path.join(tasks_dir, template_profile) with open(template_profile, 'r') as f: content = f.read() @@ -559,19 +682,34 @@ def make_profiles(agent_names, models, apis, template_profile="profiles/collab_p else: profile["model"] = models[index] - with open(f"{agent_names[index]}.json", 'w') as f: - json.dump(profile, f, indent=4) + # Save profiles inside tasks/profiles/ + profiles_output_dir = os.path.join(tasks_dir, "profiles") + os.makedirs(profiles_output_dir, exist_ok=True) + profile_name = f"{agent_names[index]}.json" + profile_path = os.path.join(profiles_output_dir, profile_name) + + with open(profile_path, 'w', encoding='utf-8') as outfile: + json.dump(profile, outfile, indent=4) def create_server_files(source_path, num_copies, world_name="Forest"): - """Create multiple copies of server files for parallel experiments.""" - print("Creating server files...") - print(num_copies) - servers = [] + """Create multiple copies of the server files inside tasks/server_data.""" + servers = [] # Define servers list + # Ensure source_path is relative to project_root if not absolute + if not os.path.isabs(source_path): + source_path = os.path.join(project_root, source_path) + + # Base dir inside tasks/ + server_base_dir = os.path.join(tasks_dir, "server_data") + os.makedirs(server_base_dir, exist_ok=True) + for i in range(num_copies): - dest_path = f"./server_data_{i}/" + # Server copies go into tasks/server_data/0/, tasks/server_data/1/, etc. + dest_path = os.path.join(server_base_dir, str(i)) copy_server_files(source_path, dest_path) print(dest_path) - edit_file(dest_path + "server.properties", {"server-port": 55916 + i, + # Adjust path for edit_file + server_prop_path = os.path.join(dest_path, "server.properties") + edit_file(server_prop_path, {"server-port": 55916 + i, "level-name": world_name}) # edit_server_properties_file(dest_path, 55916 + i) servers.append((dest_path, 55916 + i)) @@ -593,13 +731,24 @@ def edit_file(file, content_dict): print(f"Error editing file {file}: {e}") def clean_up_server_files(num_copies): - """Delete server files from multiple locations.""" + """Delete server files from multiple locations within tasks/server_data.""" + server_base_dir = os.path.join(tasks_dir, "server_data") for i in range(num_copies): - dest_path = f"./server_data_{i}/" + # Target paths like tasks/server_data/0/ + dest_path = os.path.join(server_base_dir, str(i)) delete_server_files(dest_path) def copy_server_files(source_path, dest_path): - """Copy server files to the specified location.""" + """Copy server files from source to destination (dest assumed relative to tasks_dir if not absolute).""" + # Ensure source_path is relative to project_root if not absolute + if not os.path.isabs(source_path): + source_path = os.path.join(project_root, source_path) + # Destination path is now expected inside tasks/server_data/, handled by caller (create_server_files) + # if not os.path.isabs(dest_path): + # dest_path = os.path.join(project_root, dest_path) # OLD LOGIC + + if os.path.exists(dest_path): + shutil.rmtree(dest_path) try: shutil.copytree(source_path, dest_path) print(f"Server files copied to {dest_path}") @@ -624,12 +773,13 @@ def check_same_files(d1, d2): return True def delete_server_files(dest_path): - """Delete server files from the specified location.""" - try: + """Delete server files at the destination path (assumed relative to tasks_dir if not absolute).""" + # Path is now expected inside tasks/server_data/, handled by callers + # if not os.path.isabs(dest_path): + # dest_path = os.path.join(project_root, dest_path) # OLD LOGIC + + if os.path.exists(dest_path): shutil.rmtree(dest_path) - print(f"Server files deleted from {dest_path}") - except Exception as e: - print(f"Error deleting server files: {e}") if not os.path.exists(dest_path): print("Server files deleted successfully.") # else: @@ -638,15 +788,25 @@ def delete_server_files(dest_path): def launch_world(server_path="./server_data/", agent_names=["andy", "jill"], session_name="server", port=55916): - """Launch the Minecraft world.""" - print(f"Launching Minecraft world with port {port}...") - cmd = f"cd {server_path} && java -jar server.jar" + """Launch the Minecraft server world (server assumed inside tasks/server_data).""" + # Ensure path is relative to tasks_dir if not absolute (expecting tasks/server_data/X) + if not os.path.isabs(server_path): + server_path = os.path.join(tasks_dir, server_path) + + ops_file = os.path.join(server_path, "ops.json") # ops.json inside specific server dir + check_agent_ops(agent_names, ops_file=ops_file) + + # Launch server using tmux (cwd should be the server_path itself) + java_cmd = f"java -jar server.jar nogui" + # Create tmux session for the server subprocess.run(['tmux', 'new-session', '-d', '-s', session_name], check=True) - subprocess.run(["tmux", "send-keys", "-t", session_name, cmd, "C-m"]) - time.sleep(10) + # Send command to the server session, running from its directory + subprocess.run(["tmux", "send-keys", "-t", session_name, java_cmd, "C-m"], cwd=server_path) + print(f"Launched Minecraft world in session {session_name} from {server_path} on port {port}...") + # Add a delay and check if server started + time.sleep(20) # Increased delay if not test_server_running(port): - print("Server failed to start. Retrying...") - launch_world(server_path, agent_names, session_name, port) + print(f"Warning: Server on port {port} didn't seem to start correctly after launch.") def test_server_running(port=55916): host = 'localhost' @@ -667,73 +827,69 @@ def kill_world(session_name="server"): subprocess.run(["tmux", "kill-session", "-t", session_name]) def detach_process(command): - """ - Launches a subprocess and detaches from it, allowing it to run independently. - - Args: - command: A list of strings representing the command to execute, e.g., ['python', 'my_script.py']. - """ - - try: - # Create a new process group so the child doesn't get signals intended for the parent. - # This is crucial for proper detachment. - kwargs = {} - if sys.platform == 'win32': - kwargs.update(creationflags=subprocess.CREATE_NEW_PROCESS_GROUP) # Windows specific - - process = subprocess.Popen(command, - stdin=subprocess.PIPE, # Prevent stdin blocking - stdout=subprocess.PIPE, # Redirect stdout - stderr=subprocess.PIPE, # Redirect stderr - close_fds=True, # Close open file descriptors - **kwargs) - - print(f"Process launched with PID: {process.pid}") - return process.pid # Return the PID of the detached process - - except FileNotFoundError: - print(f"Error: Command not found: {command}") - return None - except Exception as e: - print(f"An error occurred: {e}") - return None + """Detach a process using tmux.""" + # Assume commands are run from project root if needed elsewhere + process = subprocess.Popen(command, shell=True, preexec_fn=os.setsid) # Example, might need cwd def main(): - # edit_settings("settings.js", {"profiles": ["./andy.json", "./jill.json"], "port": 55917}) - # edit_server_properties_file("../server_data/", 55917) - - parser = argparse.ArgumentParser(description='Run Minecraft AI agent experiments') - parser.add_argument('--no_launch_world', action='store_true', help='Do not launch the Minecraft world') - parser.add_argument('--task_path', default="multiagent_crafting_tasks.json", help='Path to the task file') - parser.add_argument('--num_agents', default=2, type=int, help='Number of agents to run') - parser.add_argument('--num_exp', default=1, type=int, help='Number of experiments to run') - parser.add_argument('--num_parallel', default=1, type=int, help='Number of parallel servers to run') - parser.add_argument('--exp_name', default="exp", help='Name of the experiment') - parser.add_argument('--s3', action='store_true', help='Whether to upload to s3') - parser.add_argument('--bucket_name', default="mindcraft-experiments", help='Name of the s3 bucket') - parser.add_argument('--add_keys', action='store_true', help='Create the keys.json to match the environment variables') - parser.add_argument('--template_profile', default="profiles/tasks/collab_profile.json", help='Model to use for the agents') - parser.add_argument('--model', default="gpt-4o-mini", help='Model to use for the agents') - parser.add_argument('--api', default="openai", help='API to use for the agents') - # parser.add_argument('--world_name', default="Forest", help='Name of the world') - parser.add_argument('--insecure_coding', action='store_true', help='Enable insecure coding') - parser.add_argument('--url', default="http://127.0.0.1:8000/v1") - parser.add_argument('--max_messages', default=15, type=int, help='Maximum number of messages before summarizing') - parser.add_argument('--num_examples', default=2, type=int, help='Maximum number of turns before summarizing') - parser.add_argument('--no-pruning', action='store_true', help='Disable pruning of the actions') + parser = argparse.ArgumentParser(description="Evaluate MindCraft tasks") + parser.add_argument("--task_path", type=str, default="tasks/example_tasks.json", help="Path to the task file or directory (relative to project root)") + parser.add_argument("--task_ids", type=str, nargs="+", default=None, help="Specific task IDs to run") + parser.add_argument("--num_exp", type=int, default=1, help="Number of experiments per task") + parser.add_argument("--num_agents", type=int, default=2, help="Number of agents") + parser.add_argument("--model", type=str, default="gpt-4o-mini", help="Model name") + parser.add_argument("--api", type=str, default="openai", help="API provider") + parser.add_argument("--num_parallel", type=int, default=1, help="Number of parallel experiments") + parser.add_argument("--s3", action="store_true", help="Use S3 for storage") + parser.add_argument("--bucket_name", type=str, default="mindcraft-experiments", help="S3 bucket name") + parser.add_argument("--template_profile", type=str, default="profiles/tasks/collab_profile.json", help="Template profile path") + parser.add_argument("--insecure_coding", action="store_true", help="Allow insecure coding practices") + parser.add_argument("--url", type=str, default="http://127.0.0.1:8000/v1", help="API URL") + parser.add_argument("--check_results", action="store_true", help="Only check results in the specified folder") + parser.add_argument("--servers", type=str, nargs="+", default=["local"], help="List of server directories (e.g., 0 1 2 for server_data/0, server_data/1, etc.) or 'local' for parallel local runs") + parser.add_argument("--exp_name", type=str, default="exp", help="Experiment name prefix") + parser.add_argument("--s3_path", type=str, default="", help="S3 path prefix") + parser.add_argument("--max_messages", type=int, default=15, help="Maximum messages per agent") + parser.add_argument("--num_examples", type=int, default=2, help="Number of examples for few-shot learning") + parser.add_argument("--no_pruning", action="store_true", help="Disable pruning") + parser.add_argument("--block_conversation", action="store_true", help="Block agent conversation actions") + parser.add_argument("--run_in_tmux", action="store_false", help="Run experiment directly without tmux") # Default is True args = parser.parse_args() - print(args) - if not args.no_launch_world: - try: - subprocess.run(['tmux', 'kill-server'], check=True) - except: - print("No tmux session to kill") + + # Resolve relative paths provided as arguments or defaults (relative to project root) + if not os.path.isabs(args.task_path): + args.task_path = os.path.join(project_root, args.task_path) + if not os.path.isabs(args.template_profile): + # Special handling for default profile path relative to project root + if args.template_profile.startswith("profiles/"): + args.template_profile = os.path.join(project_root, args.template_profile) + else: # Assume relative to tasks dir otherwise + args.template_profile = os.path.join(tasks_dir, args.template_profile) + + if args.check_results: + # Hardcode check_folder_results to read from project_root/experiments + check_dir = os.path.join(project_root, "experiments") + check_folder_results(check_dir) + return - # delete all server files - if not args.no_launch_world: - clean_up_server_files(args.num_parallel) - if args.add_keys: + # Default server source path relative to project_root + default_server_source = os.path.join(project_root, "server_data") + if not args.run_in_tmux: # Assuming this corresponds to needing server files + # Pass default_server_source to create_server_files + servers = create_server_files(default_server_source, args.num_parallel, world_name="Forest") # Example world name + # The rest of the logic might need adjustment if not using tmux + else: + # Logic for when run_in_tmux is True (perhaps no server creation needed here?) + # Or maybe create_server_files should always run? Adjusting based on original logic + # Let's assume server files are always needed for parallel runs + servers = create_server_files(default_server_source, args.num_parallel, world_name="Forest") # Example world name + + # delete all server files (now inside tasks/server_data) + # The clean_up_server_files function now uses the correct base path + clean_up_server_files(args.num_parallel) + + if hasattr(args, 'add_keys') and args.add_keys: # Check if arg exists before using update_keys_json() launch_parallel_experiments(args.task_path, @@ -751,7 +907,8 @@ def main(): max_messages=args.max_messages, num_examples=args.num_examples, no_pruning=args.no_pruning, - run_in_tmux=not args.no_launch_world) + block_conversation=args.block_conversation, + run_in_tmux=not args.run_in_tmux) if __name__ == "__main__": main() \ No newline at end of file diff --git a/multi_data_collection_script.py b/tasks/multi_data_collection_script.py similarity index 71% rename from multi_data_collection_script.py rename to tasks/multi_data_collection_script.py index 42b0b4f..69c36a9 100644 --- a/multi_data_collection_script.py +++ b/tasks/multi_data_collection_script.py @@ -10,6 +10,15 @@ import tqdm from analyse_results import extract_result, get_immediate_subdirectories, analyze_json_file import glob +# Calculate project root directory +project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +# Define tasks directory +tasks_dir = os.path.dirname(os.path.abspath(__file__)) + +# Define paths relative to project root (for reading) +LOGS_DIR = os.path.join(project_root, "logs") +EXPERIMENTS_DIR = os.path.join(project_root, "experiments") +BOTS_DIR = os.path.join(project_root, "bots") """ This script is intended to run the evaluation script multiple times and then automatically aggregate the @@ -71,31 +80,42 @@ def identify_success_folders(download_dir, num_agents): def run_data_collection(args): - # Set up directories - LOGS_DIR = Path("logs") - SUCCESSFUL_DIR = Path(f"successful_run_logs_{datetime.now().strftime('%Y-%m-%d')}") - FULL_RUN_LOGS_DIR = Path(f"full_run_logs_{datetime.now().strftime('%Y-%m-%d')}") - EXPERIMENTS_DIR = Path("experiments") - BOTS_DIR = Path("bots") - LOGS_DIR.mkdir(exist_ok=True) + # Set up output directories inside tasks/ + timestamp_str = datetime.now().strftime('%Y-%m-%d_%H%M%S') # Add time to avoid overwrite + SUCCESSFUL_DIR = Path(os.path.join(tasks_dir, f"successful_run_logs_{timestamp_str}")) + FULL_RUN_LOGS_DIR = Path(os.path.join(tasks_dir, f"full_run_logs_{timestamp_str}")) + # Input/state dirs (relative to project root) + logs_dir_path = Path(LOGS_DIR) + experiments_dir_path = Path(EXPERIMENTS_DIR) + bots_dir_path = Path(BOTS_DIR) + + logs_dir_path.mkdir(exist_ok=True) SUCCESSFUL_DIR.mkdir(exist_ok=True) FULL_RUN_LOGS_DIR.mkdir(exist_ok=True) - # Parse tasks and repetitions + # Parse tasks and repetitions, ensuring paths are relative to project root TASKS_TO_RUN = [] for task_spec in args.tasks: parts = task_spec.split(':') if len(parts) == 2: task_path, repeats = parts[0], int(parts[1]) + # Resolve task_path relative to project root + if not os.path.isabs(task_path): + task_path = os.path.join(project_root, task_path) TASKS_TO_RUN.append((task_path, repeats)) else: print(f"Warning: Invalid task specification '{task_spec}', expected format 'path:repeats'") - # First clear anything named Andy_ or Jill_ from the bots/ folder - for bot_dir in BOTS_DIR.glob("*"): + # Clear temp agent dirs from project_root/bots/ + for bot_dir in bots_dir_path.glob("*"): if bot_dir.name.startswith(("Andy_", "Jill_", "agent_")): shutil.rmtree(bot_dir) + # Resolve eval_script path + eval_script_path = args.eval_script + if not os.path.isabs(eval_script_path): + eval_script_path = os.path.join(project_root, eval_script_path) + run_counter = 1 for task_path, repeats in TASKS_TO_RUN: for rep in range(repeats): @@ -103,38 +123,40 @@ def run_data_collection(args): print(f"\n Starting {task_path} (rep {rep + 1}/{repeats}) -> {run_id}") # Track start time to locate experiment folder - before = set(EXPERIMENTS_DIR.glob("*")) + # Ensure EXPERIMENTS_DIR is treated as Path object if needed + before = set(experiments_dir_path.glob("*")) - # Run evaluation + # Run evaluation using the resolved eval_script_path + # Run from project root subprocess.run([ - "python", args.eval_script, + "python", eval_script_path, "--api", args.api, "--model", args.model, - "--task_path", task_path, + "--task_path", task_path, # task_path is already absolute or resolved "--num_agents", str(args.num_agents), "--num_parallel", str(args.num_parallel) - ], check=True) + ], check=True, cwd=project_root) - # Wait for experiment folder to appear + # Wait for experiment folder to appear in project_root/experiments/ time.sleep(20) # avoid race condition - after = set(EXPERIMENTS_DIR.glob("*")) + after = set(experiments_dir_path.glob("*")) new_experiments = list(after - before) assert len(new_experiments) == 1, f"Expected one new experiment folder, found {len(new_experiments)}" experiment_dir = new_experiments[0] print(f"Found experiment folder: {experiment_dir}") - # Identify successful experiments + # Identify successful experiments from project_root/experiments/... successful_exp_list = identify_success_folders(experiment_dir, args.num_agents) - # Save successful logs and results + # Save successful logs and results (read from project_root/bots, write to tasks/successful_...) success_output_dir = SUCCESSFUL_DIR / run_id success_output_dir.mkdir(parents=True, exist_ok=True) # Identify the ones that are successful for exp_path in successful_exp_list: exp_name = os.path.basename(exp_path) # For each agent, find and copy their logs for this successful experiment - for bot_dir in BOTS_DIR.glob("*"): + for bot_dir in bots_dir_path.glob("*"): if bot_dir.name.startswith(("Andy_", "Jill_", "agent_")): agent_logs_dir = bot_dir / "logs" if agent_logs_dir.exists(): @@ -147,10 +169,10 @@ def run_data_collection(args): shutil.copytree(exp_dir, dest_dir) print(f"Copied successful log directory: {exp_dir} -> {dest_dir}") - # Move full logs to the full logs dir, aka anything named Jill_ or Andy_ + # Move full logs to the full logs dir (read from project_root/bots, write to tasks/full_...) full_logs_dir = FULL_RUN_LOGS_DIR / run_id full_logs_dir.mkdir(parents=True, exist_ok=True) - for bot_dir in BOTS_DIR.glob("*"): + for bot_dir in bots_dir_path.glob("*"): if bot_dir.name.startswith(("Andy_", "Jill_", "agent_")): # bot_dir is already the full path, no need for agent_dir dest_dir = full_logs_dir / bot_dir.name @@ -164,7 +186,7 @@ def run_data_collection(args): if __name__ == "__main__": parser = argparse.ArgumentParser(description="Run multiple evaluations and collect successful logs") - parser.add_argument("--eval_script", default="evaluation_script.py", help="Path to evaluation script") + parser.add_argument("--eval_script", default="tasks/evaluation_script.py", help="Path to evaluation script relative to project root") parser.add_argument("--api", default="vllm", help="API to use") parser.add_argument("--model", default="meta-llama/Meta-Llama-3-8B-Instruct", help="Model to use") parser.add_argument("--num_agents", type=int, default=2, help="Number of agents") diff --git a/multiagent_crafting_tasks.json b/tasks/multiagent_crafting_tasks.json similarity index 100% rename from multiagent_crafting_tasks.json rename to tasks/multiagent_crafting_tasks.json