anchor.diff:      return rankMap;
   }
   
  -Map<int, Set<Point>> connectedColors(List<List<Cell>> board){
  -    Map<int, Set<Point>> blobs = new Map<int, Set<Point>>();
  -    Map<int, Set<int>> equivs = new Map<int, Set<int>>();
  -    DisJointPoints groups = new DisJointPoints();
  -    int groupIndex = 0;
  -    //TODO scan map, build blob and equivalency list
  -    //first pass
  +List<Set<Point>> connectedColors(List<List<Cell>> board){
  +    List<Set<Point>> blobs = new List<Set<Point>>();
  +    Set<Point> allBlobbed = new Set<Point>();
       for(int row = 0; row < 6; row++){
           for(int col = 0; col < board[row].length; col++){
  +            //TODO reduce duplicate scanning with closed set
               Point cp = new Point(col, row);
  -            Cell current = board[row][col];
  -            if(current != Cell.skull){
  -                bool matchesNorth = false;
  -                Point northPoint = new Point(col, row - 1);
  -                bool matchesWest = false;
  -                Point westPoint = new Point(col - 1, row);
  -                if(row > 0 && cellAt(board, northPoint) == current){
  -                    matchesNorth = true;
  -                }
  -                if(col > 0 && cellAt(board, westPoint) == current){
  -                    matchesWest = true;
  -                }
  -                if(!matchesNorth && !matchesWest){
  -                    //new group discovered
  -                    // stderr.writeln("Creating blob group $groupIndex");
  -                    blobs[groupIndex] = new Set.from([cp]);
  -                    equivs[groupIndex] = new Set.from([groupIndex]);
  -                    groups[cp] = groupIndex;
  -                    //increment the label index
  -                    groupIndex++;
  -                } else if (matchesNorth && !matchesWest){
  -                    int group = groups[northPoint];
  -                    // stderr.writeln("Matching north blob group $group");
  -                    blobs[group].add(cp);
  -                    groups[cp] = group;
  -                    groups.union(northPoint, cp);
  -                } else if (!matchesNorth && matchesWest) {
  -                    int group = groups[westPoint];
  -                    // stderr.writeln("Matching west blob group $group");
  -                    blobs[group].add(cp);
  -                    groups[cp] = group;
  -                    groups.union(westPoint, cp);
  -                } else if (matchesNorth && matchesWest && groups[northPoint] == groups[westPoint]) {
  -                    //current point matches north and west and they're already the same label
  -                    // p p
  -                    // p X
  -                    int group = groups[westPoint];
  -                    // stderr.writeln("Matching west and north blob group $group");
  -                    blobs[group].add(cp);
  -                    groups[cp] = group;
  -                    groups.union(westPoint, cp);
  -                } else {
  -                    //current point matches north and west, but they are different labels
  -                    // b p
  -                    // p X
  -
  -                    //add to lowest label
  -                    int northGroup = groups[northPoint];
  -                    int westGroup = groups[westPoint];
  -                    int minGroup = min(northGroup, westGroup);
  -                    groups[northPoint] = minGroup;
  -                    groups[westPoint] = minGroup;
  -                    blobs[minGroup].add(cp);
  -                    groups.union(northPoint, westPoint);
  -                    groups.union(northPoint, cp);
  -
  -                    //update equivs
  -                    // union both sets of labels together
  -                    // for each group in labels, set equivs[group] to unioned list
  -                    Set<int> allEquivs = new Set<int>();
  -                    allEquivs.addAll(equivs[northGroup]);
  -                    allEquivs.addAll(equivs[westGroup]);
  -
  -                    allEquivs.forEach((equiv) => equivs[equiv] = allEquivs);
  -                }
  -            }
  -        }
  -    }
  -
  -    //TODO second pass
  -    //reduce blobs with same equivs
  -
  +            if(allBlobbed.contains(cp) || cellAt(board, cp) == Cell.skull){
  +                //already in a blob or not a color, skip it.
  +                continue;
  +            }
  +            Set<Point> blob = connected(board, cp);
  +            allBlobbed.addAll(blob);
  +            if(blob.length > 1){
  +                blobs.add(blob);
  +            }
  +        }
  +    }
       return blobs;
   }
   
  
positive.code:                 }
                  removals.addAll(bottomBlob);
                  blocksCleared += bottomBlob.length;
                  groupBonus += bottomBlob.length - 4;
              }
          }
  
          if(removals.length > 0 ){
              stderr.writeln("Found blobs to remove");
              // TODO search for skulls, add to remove list
  
              //stderr.writeln("Removals $removals");
              //mark removals
              removals.forEach((removal) =>
                  newBoard[removal.y][removal.x] = Cell.empty
              );
              //remove
              for(List<Cell> cells in newBoard){
                  for(int index = 0; index < cells.length; index++ ){
                      if(cells[index] == Cell.empty){
                          cells.removeAt(index);
                          index--;
                      }
                  }
              }
  
              //TODO fix score update logic
              int scoreUpdate = 10 * blocksCleared *
                  max(1, chainPower + colorBonus + groupBonus);
              //stderr.writeln("Score update for move $scoreUpdate");
              updated.score += scoreUpdate;
  
              //TODO repeat until no blobs
  
  
          }
          return updated;
  
  
negative.code:         for(int col = 0; col < board[row].length; col++){
              Point cp = new Point(col, row);
              Cell current = board[row][col];
              List<Point> valids = validNeighbors(board, cp);
              if(current != Cell.empty &&
              current != Cell.skull &&
              valids.where((valid) => cellAt(board, valid) == current).length == 1){
  