package edu.stanford.nlp.sempre.interactive.voxelurn; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; import com.google.common.collect.Sets; import edu.stanford.nlp.sempre.ContextValue; import edu.stanford.nlp.sempre.Json; import edu.stanford.nlp.sempre.NaiveKnowledgeGraph; import edu.stanford.nlp.sempre.StringValue; import edu.stanford.nlp.sempre.interactive.Item; import edu.stanford.nlp.sempre.interactive.World; import fig.basic.Option; // the world of stacks public class VoxelWorld extends World { public static class Options { @Option(gloss = "maximum number of cubes to convert") public int maxBlocks = 1024 ^ 2; } public static Options opts = new Options(); public final static String SELECT = "S"; public static VoxelWorld fromContext(ContextValue context) { if (context == null || context.graph == null) { return fromJSON("[[3,3,1,\"gray\",[\"S\"]],[4,4,1,\"blue\",[]]]"); } NaiveKnowledgeGraph graph = (NaiveKnowledgeGraph) context.graph; String wallString = ((StringValue) graph.triples.get(0).e1).value; return fromJSON(wallString); } public void base(int x, int y) { Voxel basecube = new Voxel(x, y, 0, Color.Fake.toString()); this.allItems = new HashSet<>(this.allItems); this.selected = new HashSet<>(this.selected); allItems.add(basecube); selected.add(basecube); } public Set<Item> origin() { for (Item i : allItems) { Voxel b = (Voxel) i; if (b.col == 0 && b.row == 0 & b.height == 0) return Sets.newHashSet(b); } Voxel basecube = new Voxel(0, 0, 0, Color.Fake.toString()); return Sets.newHashSet(basecube); } @SuppressWarnings("unchecked") public VoxelWorld(Set<Item> blockset) { super(); this.allItems = blockset; this.selected = blockset.stream().filter(b -> ((Voxel) b).names.contains(SELECT)).collect(Collectors.toSet()); this.selected.forEach(i -> i.names.remove(SELECT)); } // we only use names S to communicate with the client, internally its just the // select variable @Override public String toJSON() { // selected thats no longer in the world gets nothing // allitems.removeIf(c -> ((Block)c).color == CubeColor.Fake && // !this.selected.contains(c)); // allitems.stream().filter(c -> selected.contains(c)).forEach(i -> // i.names.add(SELECT)); return Json.writeValueAsStringHard(allItems.stream().map(c -> { Voxel b = ((Voxel) c).clone(); if (selected.contains(b)) b.names.add("S"); return b.toJSON(); }).collect(Collectors.toList())); // return this.worldlist.stream().map(c -> c.toJSON()).reduce("", (o, n) -> // o+","+n); } private static VoxelWorld fromJSON(String wallString) { @SuppressWarnings("unchecked") List<List<Object>> cubestr = Json.readValueHard(wallString, List.class); Set<Item> cubes = cubestr.stream().map(c -> { return Voxel.fromJSONObject(c); }).collect(Collectors.toSet()); // throw new RuntimeException(a.toString()+a.get(1).toString()); VoxelWorld world = new VoxelWorld(cubes); // world.previous.addAll(world.selected); // we can only use previous within a block; return world; } @Override public Set<Item> has(String rel, Set<Object> values) { // LogInfo.log(values); return this.allItems.stream().filter(i -> values.contains(i.get(rel))).collect(Collectors.toSet()); } @Override public Set<Object> get(String rel, Set<Item> subset) { return subset.stream().map(i -> i.get(rel)).collect(Collectors.toSet()); } @Override public void update(String rel, Object value, Set<Item> selected) { selected.forEach(i -> i.update(rel, value)); keyConsistency(); } // if selected is no longer in all, make it fake colored, and add to all; // likewise, if some fake colored block is no longer selected, remove it @Override public void merge() { Sets.difference(selected, allItems).forEach(i -> ((Voxel) i).color = Color.Fake); allItems.removeIf(c -> ((Voxel) c).color.equals(Color.Fake) && !this.selected.contains(c)); allItems.addAll(selected); if (allItems.size() > opts.maxBlocks) { throw new RuntimeException( String.format("Number of blocks (%d) exceeds the upperlimit %d", allItems.size(), opts.maxBlocks)); } // keyConsistency(); } // block world specific actions, overriding move public void move(String dir, Set<Item> selected) { // allitems.removeAll(selected); selected.forEach(b -> ((Voxel) b).move(Direction.fromString(dir))); keyConsistency(); // allitems.addAll(selected); // this is not overriding } public void add(String colorstr, String dirstr, Set<Item> selected) { Direction dir = Direction.fromString(dirstr); Color color = Color.fromString(colorstr); if (dir == Direction.None) { // add here selected.forEach(b -> ((Voxel) b).color = color); } else { Set<Item> extremeCubes = extremeCubes(dir, selected); this.allItems.addAll(extremeCubes.stream().map(c -> { Voxel d = ((Voxel) c).copy(dir); d.color = color; return d; }).collect(Collectors.toList())); } } // get cubes at extreme positions public Set<Item> veryx(String dirstr, Set<Item> selected) { Direction dir = Direction.fromString(dirstr); switch (dir) { case Back: return argmax(c -> c.row, selected); case Front: return argmax(c -> -c.row, selected); case Left: return argmax(c -> c.col, selected); case Right: return argmax(c -> -c.col, selected); case Top: return argmax(c -> c.height, selected); case Bot: return argmax(c -> -c.height, selected); default: throw new RuntimeException("invalid direction"); } } // return retrieved from allitems, along with any potential selectors which // are empty. public Set<Item> adj(String dirstr, Set<Item> selected) { Direction dir = Direction.fromString(dirstr); Set<Item> selectors = selected.stream().map(c -> { Voxel b = ((Voxel) c).copy(dir); b.color = Color.Fake; return b; }).collect(Collectors.toSet()); this.allItems.addAll(selectors); Set<Item> actual = allItems.stream().filter(c -> selectors.contains(c)).collect(Collectors.toSet()); return actual; } public static Set<Item> argmax(Function<Voxel, Integer> f, Set<Item> items) { int maxvalue = Integer.MIN_VALUE; for (Item i : items) { int cvalue = f.apply((Voxel) i); if (cvalue > maxvalue) maxvalue = cvalue; } final int maxValue = maxvalue; return items.stream().filter(c -> f.apply((Voxel) c) >= maxValue).collect(Collectors.toSet()); } @Override public void noop() { keyConsistency(); } // get cubes at the outer locations private Set<Item> extremeCubes(Direction dir, Set<Item> selected) { Set<Item> realCubes = realBlocks(allItems); return selected.stream().map(c -> { Voxel d = (Voxel) c; while (realCubes.contains(d.copy(dir))) d = d.copy(dir); return d; }).collect(Collectors.toSet()); } // ensures key coherence on mutations private void refreshSet(Set<Item> set) { List<Item> s = new LinkedList<>(set); set.clear(); set.addAll(s); } private void keyConsistency() { refreshSet(allItems); refreshSet(selected); refreshSet(previous); } private Set<Item> realBlocks(Set<Item> all) { return all.stream().filter(b -> !((Voxel) b).color.equals(Color.Fake)).collect(Collectors.toSet()); } }