Exit Full View
ZipUp

/Games/Wooden Block Puzzle/WoodenBlock.foocad

WoodenBlock

Generates a puzzle piece for the classic "Wooden" Block puzzle. Take 6 pieces, and arrange them into a 3x3x3 cube.

See Generate.feather for a script which generates all the pieces. Note, you do not need a full set of pieces. 6 is all you need for a single puzzle.

See Solver.kts for a script which solves the puzzles, so that you can see if a combination of pieces has a solution and if so, how easy it is (the more solutions there are, the easier it is).

For a hard puzzle try pieces : P E T A I D Note, the "D" piece requires support material the others do not.

FooCAD Source Code
class WoodenBlock : AbstractModel() {
   
    // The name of the piece. These are the names which look a little like
    // the shape. They are NOT the letters that appear on the blocks.
    // Values  ALL  1 2 3 V    A I O E Y U S   D P R T N L     H B X W K J G M F C
    // See rename
    @Custom
    var piece = "1"

    // The size of the cube
    @Custom
    var size = 20

    // Make the pieces slightly smaller, so that they fit together.
    @Custom
    var gap = 0.3

    @Custom
    var radius = 2

    // 1 for a chamfer, more than 1 for more rounding.
    @Custom
    var radiusSides = 3

    @Custom
    var embossDepth = 1.2

    @Custom
    var embossThicker = 0.2


    // We want a "sphere", whose shadow is the same from either X, Y or Z direction.
    // A "normal" sphere won't do, as we cannot control the "sides" in the x, y and z.
    // So here we revolve a semi circle, where the number of sides of the semicircle
    // and number of sides of the revolution are the same.
    fun rounding() : Shape3d {
        //return Sphere( radius, radiusSides ) // A normal sphere won't do!
        return PolygonBuilder().apply {
            moveTo(0,-radius)
            lineTo(radius,-radius)
            lineTo(radius,radius)
            lineTo(0,radius)
            close()
        }.build()
        .roundCorner(2, radius, radiusSides )
        .roundCorner(1, radius, radiusSides )
        .revolve().sides(radiusSides*4)
    }
    

    // ghi
    // def
    // abc
    fun voxel( position: char ) : Shape3d {
        val lower = position.toLowerCase()
        val i = lower.toInt() - 'a'.toInt()
        val x = i % 3
        val y = i ~/ 3
        val z = if (position.isLowerCase()) 0 else 1
        return Cube(size - gap - radius*2).translate( x*size, y*size, z*size )
    }

    fun piece( info : String, color: String, letter : String ) : Shape3d {

        val pieces = listOf<Shape3d>()

        for (item in info.split(" ")) {
            val fromC = item.charAt(0)
            val toC = item.charAt(1)
            pieces.add( voxel(fromC).hull( voxel(toC ) ) )
            1
        }

        val union = Union3d( pieces )
        val result = union.minkowski( rounding() ).translate(radius, radius, radius )

        val emboss = Text( letter, size * 0.65 ).hAlign(CENTER)
            .mirrorX()
            .offset( embossThicker )
            .extrude( embossDepth )
            .translate(size*1.5,size*0.2,-0.1)

        return result.color( color ) - emboss.color("White")
    }

    // ghi
    // def
    // abc
    fun piece( name : String ) : Shape3d {
        val small = "Silver"
        val four = "Red"
        val five = "Orange"
        val extra1 = "Yellow"
        val extra2 = "Green"

        if (name == "1") return piece( "bb", small, name )
        if (name == "2") return piece( "ab", small, name )
        if (name == "3") return piece( "ac", small, name )
        if (name == "v") return piece( "ab be", small, name )

        if (name == "A") return piece( "be ef fF", four, name )
        if (name == "I") return piece( "ab be eE", four, name )
        if (name == "O") return piece( "ab be ed da", four, name )
        if (name == "E") return piece( "ac cf", four, name )
        if (name == "Y") return piece( "ab be ef", four, name )
        if (name == "U") return piece( "ac be", four, name )
        if (name == "S") return piece( "ab ad aA", four, name )

        if (name == "D") return piece( "cb be eE ED", five, name )
        if (name == "R") return piece( "ac ad dD", five, name )
        if (name == "P") return piece( "ac cf fF", five, name )
        if (name == "T") return piece( "ca aA be", five, name )
        if (name == "N") return piece( "ab be eE EF", five, name )
        if (name == "L") return piece( "ca cC be", five, name )

        if (name == "K") return piece( "df dg be", extra1, name )
        if (name == "B") return piece( "ac de ad be", extra1, name )
        if (name == "H") return piece( "bh df", extra1, name )
        if (name == "W") return piece( "ac be bB", extra1, name )
        if (name == "C") return piece( "da ac cf", extra1, name )
        if (name == "J") return piece( "aA ac cf", extra2, name )
        if (name == "G") return piece( "da ac cC", extra2, name )
        if (name == "Z") return piece( "ab bh hi", extra2, name )
        if (name == "M") return piece( "ab ad de eb aA", extra2, name )
        if (name == "F") return piece( "ac cC cf", extra2, name )


        return piece( "aa", "Silver", "." )
        
    }

    override fun build() = piece( piece )

}