Quellcode durchsuchen

additions to the collision system(modified method), tile and entity collision, test tiles

Kajetan Johannes Hammerle vor 6 Jahren
Ursprung
Commit
e37b3c8cfa

+ 293 - 28
src/me/hammerle/supersnuvi/entity/Entity.java

@@ -1,5 +1,6 @@
 package me.hammerle.supersnuvi.entity;
 
+import java.util.Collections;
 import java.util.LinkedList;
 import javafx.scene.image.Image;
 import javafx.scene.image.PixelWriter;
@@ -8,8 +9,9 @@ import javafx.scene.paint.Color;
 import me.hammerle.supersnuvi.input.IKeyHandler;
 import me.hammerle.supersnuvi.rendering.GameRenderer;
 import me.hammerle.supersnuvi.rendering.WorldRenderer;
-import me.hammerle.supersnuvi.tiles.Tile;
+import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.Utils;
 
 public class Entity
@@ -21,14 +23,16 @@ public class Entity
     // added correction factor, real life gravity seems to strong
     public static double GRAVITY = GameRenderer.TILE_SIZE * 9.81 / 60 * 0.1;
     
-    private double prevX;
-    private double prevY;
+    private double prevPosX;
+    private double prevPrevY;
     private double posX;
     private double posY;
     
-    protected double motionX;
-    protected double motionY;
-    public boolean onGround;
+    private double prevMotionX;
+    private double prevMotionY;
+    private double motionX;
+    private double motionY;
+    private boolean onGround;
     
     private CollisionBox collisionBox;
     private double width;
@@ -36,15 +40,15 @@ public class Entity
     
     private Image image;
     
-    private WorldRenderer map;
+    private WorldRenderer world;
     
     public Entity(WorldRenderer map, double x, double y, double width, double height)
     {
-        this.map = map;
+        this.world = map;
         this.posX = x;
         this.posY = y;
-        this.prevX = x;
-        this.prevY = y;
+        this.prevPosX = x;
+        this.prevPrevY = y;
         this.motionX = 0;
         this.motionY = 0;
         this.onGround = false;
@@ -68,6 +72,10 @@ public class Entity
         }
     }
 
+    //--------------------------------------------------------------------------
+    // ticking
+    //--------------------------------------------------------------------------
+    
     public void controlTick(IKeyHandler keyHandler) 
     {
     }
@@ -79,11 +87,14 @@ public class Entity
             motionY -= GRAVITY;
         }
         
-        prevX = posX;
-        prevY = posY;
+        prevMotionX = motionX;
+        prevMotionY = motionY;
+        
+        prevPosX = posX;
+        prevPrevY = posY;
         setAllowedMotion();
         posX += motionX;
-        posY += motionY;      
+        posY += motionY;   
         
         motionX = Utils.round(motionX);
         motionY = Utils.round(motionY);
@@ -95,8 +106,29 @@ public class Entity
         
         posX = Utils.round(posX);
         posY = Utils.round(posY);
+        
+        if(!onGround)
+        {
+            motionX *= 0.7;
+        }
+        
+        LinkedList<CollisionBox> list = world.getCollisionBoxesAt(this, getBox());
+        if(!list.isEmpty())
+        {
+            System.out.println(getBox());
+            list.forEach(s -> System.out.println(s));
+            System.out.println("WELL");
+            posX = prevPosX;
+            posY = prevPrevY;
+        }
+        
+        doCollision();
     }
     
+    //--------------------------------------------------------------------------
+    // gravity
+    //--------------------------------------------------------------------------
+    
     public final void jump()
     {
         if(onGround)
@@ -106,11 +138,25 @@ public class Entity
         }
     }
     
+    public boolean isAffectedByGravity()
+    {
+        return true;
+    }
+    
     public double getJumpPower()
     {
         return 0;
     }
     
+    public boolean isOnGround()
+    {
+        return onGround;
+    }
+    
+    //--------------------------------------------------------------------------
+    // collision stuff
+    //--------------------------------------------------------------------------
+    
     public final CollisionBox getBox() 
     {
         return collisionBox.reset().offset(posX, posY);
@@ -143,7 +189,7 @@ public class Entity
         // expanding area by the size of the entity
         CollisionBox expandedBox = eBox.copy().expand(motionX, motionY);
         
-        LinkedList<CollisionBox> allBoxes = map.getCollisionBoxesAt(this, expandedBox);
+        LinkedList<CollisionBox> allBoxes = world.getCollisionBoxesAt(this, expandedBox);
         if(allBoxes.isEmpty())
         {
             return;
@@ -151,6 +197,7 @@ public class Entity
         
         double x;
         double y;
+        char lastWall = 0;
                 
         if(dirX >= 0)
         {
@@ -166,15 +213,51 @@ public class Entity
                     }
                     x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY());
                     y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX());
-                    if(y >= box.getMinY() && y <= box.getMaxY())
+                    if(y > box.getMinY() && y < box.getMaxY())
                     {
                         endX = box.getMinX();
+                        endY = y;
+                        lastWall = 'x';
                     }
-                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    else if(x > box.getMinX() && x < box.getMaxX())
                     {
                         endY = box.getMaxY();
+                        endX = x;
+                        lastWall = 'y';
+                    }
+                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    {
+                        lastWall = 'y';
                     }
                 }
+                if(lastWall == 'x')
+                {
+                    double lineEndY = startY + dirY;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(endX <= box.getMinX() || lineEndY >= box.getMaxY() ||
+                            endX >= box.getMaxX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndY = box.getMaxY();
+                    }
+                    endY = lineEndY;
+                }
+                else if(lastWall == 'y')
+                {
+                    double lineEndX = startX + dirX;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(lineEndX <= box.getMinX() || endY >= box.getMaxY() ||
+                            endX >= box.getMaxX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndX = box.getMinX();
+                    }
+                    endX = lineEndX;
+                }
             }
             else // Right - Up
             {
@@ -188,15 +271,51 @@ public class Entity
                     }
                     x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY());
                     y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX());
-                    if(y >= box.getMinY() && y <= box.getMaxY())
+                    if(y > box.getMinY() && y < box.getMaxY())
                     {
                         endX = box.getMinX();
+                        endY = y;
+                        lastWall = 'x';
                     }
-                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    else if(x > box.getMinX() && x < box.getMaxX())
                     {
                         endY = box.getMinY();
+                        endX = x;
+                        lastWall = 'y';
+                    }
+                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    {
+                        lastWall = 'y';
                     }
                 }
+                if(lastWall == 'x')
+                {
+                    double lineEndY = startY + dirY;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(endX <= box.getMinX() || lineEndY <= box.getMinY() ||
+                            endX >= box.getMaxX() || endY >= box.getMaxY())
+                        {
+                            continue;
+                        }
+                        lineEndY = box.getMinY();
+                    }
+                    endY = lineEndY;
+                }
+                else if(lastWall == 'y')
+                {
+                    double lineEndX = startX + dirX;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(lineEndX <= box.getMinX() || endY >= box.getMaxY() ||
+                            endX >= box.getMaxX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndX = box.getMinX();
+                    }
+                    endX = lineEndX;
+                }
             }
         }
         else
@@ -213,14 +332,50 @@ public class Entity
                     }
                     x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY());
                     y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX());
-                    if(y >= box.getMinY() && y <= box.getMaxY())
+                    if(y > box.getMinY() && y < box.getMaxY())
                     {
                         endX = box.getMaxX();
+                        endY = y;
+                        lastWall = 'x';
                     }
-                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    else if(x > box.getMinX() && x < box.getMaxX())
                     {
                         endY = box.getMaxY();
+                        endX = x;
+                        lastWall = 'y';
+                    }
+                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    {
+                        lastWall = 'y';
+                    }
+                }
+                if(lastWall == 'x')
+                {
+                    double lineEndY = startY + dirY;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(endX <= box.getMinX() || lineEndY >= box.getMaxY() ||
+                            endX >= box.getMaxX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndY = box.getMaxY();
+                    }
+                    endY = lineEndY;
+                }
+                else if(lastWall == 'y')
+                {
+                    double lineEndX = startX + dirX;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() ||
+                            endX <= box.getMinX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndX = box.getMaxX();
                     }
+                    endX = lineEndX;
                 }
             }
             else  // Left - Up
@@ -235,14 +390,50 @@ public class Entity
                     }
                     x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY());
                     y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX());
-                    if(y >= box.getMinY() && y <= box.getMaxY())
+                    if(y > box.getMinY() && y < box.getMaxY())
                     {
                         endX = box.getMaxX();
+                        endY = y;
+                        lastWall = 'x';
                     }
-                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    else if(x > box.getMinX() && x < box.getMaxX())
                     {
                         endY = box.getMinY();
+                        endX = x;
+                        lastWall = 'y';
+                    }
+                    else if(x >= box.getMinX() && x <= box.getMaxX())
+                    {
+                        lastWall = 'y';
+                    }
+                }
+                if(lastWall == 'x')
+                {
+                    double lineEndY = startY + dirY;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(endX <= box.getMinX() || lineEndY <= box.getMinY() ||
+                            endX >= box.getMaxX() || endY >= box.getMaxY())
+                        {
+                            continue;
+                        }
+                        lineEndY = box.getMinY();
                     }
+                    endY = lineEndY;
+                }
+                else if(lastWall == 'y')
+                {
+                    double lineEndX = startX + dirX;
+                    for(CollisionBox box : allBoxes)
+                    {
+                        if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() ||
+                            endX <= box.getMinX() || endY <= box.getMinY())
+                        {
+                            continue;
+                        }
+                        lineEndX = box.getMaxX();
+                    }
+                    endX = lineEndX;
                 }
             }
         }
@@ -261,21 +452,44 @@ public class Entity
             System.exit(0);
         }
         
-        System.out.println("VORHER " + dirX + " " + dirY);
-        System.out.println("NACHHER " + motionX + " " + motionY);
+        /*if(!onGround && Math.abs(dirY) - Math.abs(motionY) > 1)
+        {
+            System.out.println("\nVORHER " + dirX + " " + dirY);
+            System.out.println("NACHHER " + motionX + " " + motionY);
+            System.out.println("\nPOS " + posX + " " + posY);
+            System.exit(0);
+        }*/
         //System.exit(0);
     }
     
-    public final Image getImage()
+    private void doCollision()
     {
-        return image;
+        CollisionBox box = getBox().copy();
+        for(Face f : Face.values())
+        {
+            box.reset();
+            box.expand(f.getCollisionOffsetX(), f.getCollisionOffsetY());
+            world.getEntitiesCollidingWith(this, box).forEach(ent -> this.onCollideWithEntity(ent, f));
+            world.getTilesCollidingWith(box).forEach(loc -> 
+            {
+                this.onCollideWithTile(loc, f);
+                loc.getTile().onEntityCollide(this, loc.getX(), loc.getY(), f);
+            });
+        }
     }
     
-    public boolean isAffectedByGravity()
+    public void onCollideWithTile(Location loc, Face face)
     {
-        return true;
     }
     
+    public void onCollideWithEntity(Entity ent, Face face)
+    {
+    }
+    
+    //--------------------------------------------------------------------------
+    // basic coord and motion stuff
+    //--------------------------------------------------------------------------
+    
     public final double getX()
     {
         return posX;
@@ -286,6 +500,52 @@ public class Entity
         return posY;
     }
     
+    public final double getPreviousX()
+    {
+        return prevPosX;
+    }
+    
+    public final double getPreviousY()
+    {
+        return prevPrevY;
+    }
+    
+    public final double getMotionX()
+    {
+        return motionX;
+    }
+    
+    public final void setMotionX(double motionX)
+    {
+        prevMotionX = this.motionX;
+        this.motionX = motionX;
+    }
+    
+    public final double getMotionY()
+    {
+        return motionY;
+    }
+    
+    public final void setMotionY(double motionY)
+    {
+        prevMotionY = this.motionY;
+        this.motionY = motionY;
+    }
+    
+    public final double getPreviousMotionX()
+    {
+        return prevMotionX;
+    }
+    
+    public final double getPreviousMotionY()
+    {
+        return prevMotionY;
+    }
+    
+    //--------------------------------------------------------------------------
+    // rendering stuff
+    //--------------------------------------------------------------------------
+    
     public final double getRenderX()
     {
         return posX;
@@ -295,4 +555,9 @@ public class Entity
     {
         return posY + height;
     }
+    
+    public final Image getImage()
+    {
+        return image;
+    }
 }

+ 16 - 4
src/me/hammerle/supersnuvi/entity/Hero.java

@@ -25,19 +25,31 @@ public class Hero extends Entity
         if(keyHandler.isKeyDown(KeyCode.SPACE))
         {
             jump();
+            /*posX = 96;
+            posY= 128;*/
         }
         
+        //motionX = -2;
+        //motionY = -2;
         if(keyHandler.isKeyDown(KeyCode.LEFT))
         {
-            motionX = -2;
+            setMotionX(-2);
         }
         else if(keyHandler.isKeyDown(KeyCode.RIGHT))
         {
-            motionX = 2;
+            setMotionX(2);
         }
-        else
+        /*if(keyHandler.isKeyDown(KeyCode.UP))
+        {
+            setMotionY(2);
+        }
+        else if(keyHandler.isKeyDown(KeyCode.DOWN))
         {
-            motionX = 0;
+            setMotionY(-2);
         }
+        else
+        {
+            setMotionY(0);
+        }*/
     }
 }

+ 35 - 8
src/me/hammerle/supersnuvi/rendering/WorldRenderer.java

@@ -2,6 +2,7 @@ package me.hammerle.supersnuvi.rendering;
 
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.stream.Collectors;
 import javafx.scene.image.Image;
 import javafx.scene.image.WritableImage;
@@ -9,6 +10,9 @@ import javafx.scene.paint.Color;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.Hero;
 import me.hammerle.supersnuvi.input.IKeyHandler;
+import me.hammerle.supersnuvi.tiles.Ice;
+import me.hammerle.supersnuvi.tiles.TrampolinTile;
+import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Utils;
@@ -47,8 +51,8 @@ public class WorldRenderer
         this.registeredTiles = new HashMap<>();
         this.entities = new HashMap<>();
         
-        Entity ent = new Entity(this, 60, 240, 32, 60);
-        Hero hero = new Hero(this, 0, 160, 32, 60);
+        Entity ent = new Entity(this, 80, 340, 32, 64);
+        Hero hero = new Hero(this, 32, 100, 32, 64);
         
         entities.put(1, ent);
         entities.put(2, hero);
@@ -67,9 +71,8 @@ public class WorldRenderer
         registeredTiles.put(2, new Tile("dirt").setDefaultCollisionBox());
         registeredTiles.put(3, new Tile("grass").setDefaultCollisionBox());
         
-        registerTile(4, 255, 0, 0, 255);
-        registeredTiles.get(4).setCollisionBox(CollisionBox.DEFAULT_BOX);
-        registerTile(5, 255, 0, 255, 255);
+        registeredTiles.put(4, new TrampolinTile().setDefaultCollisionBox());
+        registeredTiles.put(5, new Ice().setDefaultCollisionBox());
         registerTile(6, 255, 255, 0, 255);
         registerTile(7, 255, 255, 255, 255);
     }
@@ -206,14 +209,38 @@ public class WorldRenderer
         return tile.getCollisionBox().reset().offset(renderer.toCoord(x), renderer.toCoord(y));
     }
     
+    public LinkedList<Location> getTilesCollidingWith(CollisionBox cb)
+    {
+        LinkedList<Location> boxes = new LinkedList<>();
+        int startX = renderer.toBlock(cb.getMinX());
+        int endX = renderer.toBlock(cb.getMaxX());
+        int startY = renderer.toBlock(cb.getMinY());
+        int endY = renderer.toBlock(cb.getMaxY());
+        
+        for(int x = startX; x <= endX; x++)
+        {
+            for(int y = startY; y <= endY; y++)
+            {
+                if(getTileCollisionBox(x, y).intersects(cb))
+                {
+                    boxes.add(new Location(getInteractionTile(x, y), x, y));
+                }
+            }
+        }
+        return boxes;
+    }
+    
+    public List<Entity> getEntitiesCollidingWith(Entity not, CollisionBox cb)
+    {
+        return entities.values().stream().filter(ent -> ent != not && ent.getBox().intersects(cb)).collect(Collectors.toList());
+    }
+    
     public LinkedList<CollisionBox> getCollisionBoxesAt(Entity not, CollisionBox cb)
     {
-        LinkedList<CollisionBox> boxes = new LinkedList<>(
-                entities.values().stream()
+        LinkedList<CollisionBox> boxes = new LinkedList<>(entities.values().stream()
                         .filter(ent -> ent != not && ent.getBox().intersects(cb))
                         .map(ent -> ent.getBox())
                         .collect(Collectors.toList()));
-        
         int startX = renderer.toBlock(cb.getMinX());
         int endX = renderer.toBlock(cb.getMaxX());
         int startY = renderer.toBlock(cb.getMinY());

+ 30 - 0
src/me/hammerle/supersnuvi/tiles/Ice.java

@@ -0,0 +1,30 @@
+package me.hammerle.supersnuvi.tiles;
+
+import javafx.scene.paint.Color;
+import me.hammerle.supersnuvi.entity.Entity;
+import me.hammerle.supersnuvi.util.Face;
+
+public class Ice extends Tile
+{
+    public Ice() 
+    {
+        super(new Color(0.5f, 0.5f, 0.5f, 1));
+    }
+
+    @Override
+    public void onEntityCollide(Entity ent, int x, int y, Face face) 
+    {
+        if(face == Face.UP)
+        {
+            double motionX = ent.getMotionX() * 0.98d;
+            if(Math.abs(motionX) > 0.1)
+            {
+                ent.setMotionX(motionX);
+            }
+            else
+            {
+                ent.setMotionX(0);
+            }
+        }
+    }
+}

+ 30 - 0
src/me/hammerle/supersnuvi/tiles/Location.java

@@ -0,0 +1,30 @@
+package me.hammerle.supersnuvi.tiles;
+
+public class Location 
+{
+    private Tile tile;
+    private int x;
+    private int y;
+    
+    public Location(Tile tile, int x, int y)
+    {
+        this.tile = tile;
+        this.x = x;
+        this.y = y;        
+    }
+
+    public Tile getTile() 
+    {
+        return tile;
+    }
+
+    public int getX() 
+    {
+        return x;
+    }
+
+    public int getY() 
+    {
+        return y;
+    }
+}

+ 18 - 0
src/me/hammerle/supersnuvi/tiles/Tile.java

@@ -4,8 +4,10 @@ import javafx.scene.image.Image;
 import javafx.scene.image.PixelWriter;
 import javafx.scene.image.WritableImage;
 import javafx.scene.paint.Color;
+import me.hammerle.supersnuvi.entity.Entity;
 import static me.hammerle.supersnuvi.rendering.GameRenderer.TILE_SIZE;
 import me.hammerle.supersnuvi.util.CollisionBox;
+import me.hammerle.supersnuvi.util.Face;
 
 public class Tile 
 {
@@ -63,4 +65,20 @@ public class Tile
         }
         return CollisionBox.NULL_BOX.reset();
     }
+    
+    public void onEntityCollide(Entity ent, int x, int y, Face face)
+    {
+        if(face == Face.UP)
+        {
+            double motionX = ent.getMotionX() * 0.7d;
+            if(Math.abs(motionX) > 0.2)
+            {
+                ent.setMotionX(motionX);
+            }
+            else
+            {
+                ent.setMotionX(0);
+            }
+        }
+    }
 }

+ 31 - 0
src/me/hammerle/supersnuvi/tiles/TrampolinTile.java

@@ -0,0 +1,31 @@
+package me.hammerle.supersnuvi.tiles;
+
+import javafx.scene.paint.Color;
+import me.hammerle.supersnuvi.entity.Entity;
+import me.hammerle.supersnuvi.util.Face;
+
+public class TrampolinTile extends Tile
+{
+    public TrampolinTile() 
+    {
+        super(new Color(1, 1, 1, 1));
+    }
+
+    @Override
+    public void onEntityCollide(Entity ent, int x, int y, Face face) 
+    {
+        super.onEntityCollide(ent, x, y, face);
+        if(face == Face.UP)
+        {
+            double motionY = ent.getPreviousMotionY();
+            if(motionY < 0)
+            {
+                motionY *= -0.9;
+                if(motionY > 1)
+                {
+                    ent.setMotionY(motionY);
+                }
+            }
+        }
+    }
+}

+ 40 - 0
src/me/hammerle/supersnuvi/util/Face.java

@@ -0,0 +1,40 @@
+package me.hammerle.supersnuvi.util;
+
+public enum Face 
+{
+    LEFT(0.01d, 0),
+    RIGHT(-0.01d, 0),
+    UP(0, -0.01d),
+    DOWN(0, 0.01d);
+    
+    private double offsetX;
+    private double offsetY;
+    
+    Face(double offsetX, double offsetY)
+    {
+        this.offsetX = offsetX;
+        this.offsetY = offsetY;
+    }
+    
+    public Face getOpposite()
+    {
+        switch(this)
+        {
+            case LEFT: return RIGHT;
+            case RIGHT: return LEFT;
+            case UP: return DOWN;
+            case DOWN: return UP;
+        }
+        return UP;
+    }
+    
+    public double getCollisionOffsetX()
+    {
+        return offsetX;
+    }
+    
+    public double getCollisionOffsetY()
+    {
+        return offsetY;
+    }
+}

+ 9 - 9
test.map

@@ -8,15 +8,15 @@
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
 #
-1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2
-1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2
-1, 1, 1, 2, 2, 1, 1, 1, 1, 2, 2
-1, 1, 1, 1, 2, 1, 1, 1, 3, 2, 2
-1, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2
-1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2
-3, 3, 3, 1, 1, 1, 3, 1, 3, 3, 2
-2, 2, 2, 3, 1, 3, 2, 1, 2, 2, 2
+2, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2
+2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+2, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1
+2, 1, 1, 2, 2, 3, 1, 1, 3, 1, 1
+2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1
+2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3
+2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2
+2, 3, 3, 1, 1, 1, 3, 4, 3, 3, 2
+2, 2, 2, 5, 5, 5, 2, 2, 2, 2, 2
 #
 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
 -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,