Exit Full View
Up

/Games/Super7.foocad

Super7

Super 7 is a board game based on noughts and crosses - but much better!

Read the rules here (also pasted at the end of this script): [http://nickthecoder.co.uk:8081/instructions/superSeven.html]

For a LONG game, print 9 complete sets, and arrange them in a 3x3 grid. First to win 3 games in a line wins (or a total of 5 games) ;-) I'm kidding, but also curious to know how this super super 7 game would pan out!

Print Notes

Print lots of coins (40 ish) of each colour. I chose blue and red. 4 or 5 bigCoins of each colour (the game is over if you create 5 big coins).

If you prefer, you could print only the small coins, and no bigCoins. When a tile is won, fill the whole tile with the tile winner's coins.

Each of the tiles (numbered 3 to 11). Print them three at a time using tile1..3, or all in one go using piece name "tiles".

The printer will pause when you need to change filament (twice). I used white, grey then black

You also need 2 dice.

Notes

The box has enough space to shove the coins in willy nilly, but you can also fill up each tile, add the bigCoin on top, and then slide it into one of the slots. The coins do NOT fall out ;-) So the reamining space is just for the dice.

There are two logos for the top of the box. The original game was called Super7. But that name is now used by something quite different. The box and tiles reminded me of After Eight chocolates, so I thought it amusing to call the game Around Seven.

FooCAD Source Code
import static uk.co.nickthecoder.foocad.changefilament.v1.ChangeFilament.*
import static uk.co.nickthecoder.foocad.chamferedextrude.v1.ChamferedExtrude.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout2d.*
import static uk.co.nickthecoder.foocad.layout.v1.Layout3d.*

include ThickWalledBox.foocad

class Super7 : Model {
    
    // The size of the coin determines the size of the tiles
    var coinD = 18
    var coinT = 2 // Total thickness of the small coins
    var bigCoinT = 3 // Total thickness of the big coin

    var textT = 0.6

    var baseT = 0.6
    var lineZ = 1
    var lineWidth = 1.2

    // How much bigger the squares are compared to the coin diameter
    var coinSlack = 0.8

    // How much vertical space, so that coins fit on top of the letters easily,
    // and bigCoins fit on top of coins easily.
    var zSlack = 0.3

    // For "box*" "row*", should we pause the print to change the colour of filament?    
    var changeFilament = true

    @Piece
    fun coin() : Shape3d {
        val rim = (Circle( coinD/2) - Circle( coinD/2 - 1 ))
            .extrude( textT + zSlack )
        val base = Circle( coinD / 2 ).chamferedExtrude( coinT - rim.size.z, 0.8, 0 )

        return (base + rim.bottomTo(base.top)).color("Red")
    }

    @Piece
    fun bigCoin() : Shape3d {

        val square : Shape3d = square("1")

        val coin = Circle( square.size.x * 1.3 ).sides(60)
            .chamferedExtrude( bigCoinT, 1, 0 )

        val holes = Circle( coinD/2 + coinSlack )
            .repeatX( 3, square.size.x - lineWidth )
            .repeatY( 3, square.size.y - lineWidth )
            .center()

        val lines = (Circle( coinD/2 + coinSlack ) - Circle( coinD/2 ))
            .repeatX( 3, square.size.x - lineWidth )
            .repeatY( 3, square.size.y - lineWidth )
            .center()
            .extrude( 0.3 )

        // Note, the big coin should sit on the box lines.
        // So we need voids where the small coins fit.
        // The small coins poke up by coinT - lineZ
        val result = coin -
            //lines - 
            holes.extrude( bigCoinT ).bottomTo( coin.top - coinT + lineZ - zSlack)

        return result.color( "blue" )
    }

    fun square(str: String) : Shape3d {
        val size = coinD + lineWidth*2 + coinSlack
        val profile = Square( size ).center()//.roundAllCorners(1)
        val lines = (profile - profile.offset(-1)).extrude( baseT + textT + lineZ )
            .color( "DarkGrey")

        val fontSize = if (str == "*" ) 15 else 10
        val text = Text( str, BOLD, fontSize ).toPolygon().center().extrude( baseT + textT*2 ) 
            .color( "DarkGrey" )

        return  lines + text
   } 

    @Piece
    fun tile3() = tile( "3" )

    @Piece
    fun tile4() = tile( "4" )

    @Piece
    fun tile5() = tile( "5" )

    @Piece
    fun tile6() = tile( "6" )

    @Piece
    fun tile7() = tile( "7" )

    @Piece
    fun tile8() = tile( "8" )

    @Piece
    fun tile9() = tile( "9" )

    @Piece
    fun tile10() = tile( "10" )
    
    @Piece
    fun tile11() = tile( "11" )

    @Piece
    fun row1() = row( tile3(), tile4(), tile5() )

    @Piece
    fun row2() = row( tile6(), tile7(), tile8() )

    @Piece
    fun row3() = row( tile9(), tile10(), tile11() )

    @Piece
    fun tiles() = row( row1().rotateZ(90), row2().rotateZ(90), row3().rotateZ(90) )

    fun row( a : Shape3d, b : Shape3d, c : Shape3d ) : Shape3d {
        return a.rightTo( b.left - 1 ) + b + c.leftTo( b.right + 1 )
    }

    fun tile( str : String ) : Shape3d {

        val three = square( "3" )
        val four = square( "4" ).leftTo(three.right-lineWidth)
        val five = square( "5" ).leftTo(four.right-lineWidth)
        val row1 = three + four + five

        var six = square( "6" )
        var seven = square( "*" ).leftTo(six.right-lineWidth)
        var eight = square( "8" ).leftTo(seven.right-lineWidth)
        val row2 = (six + seven + eight).backTo( row1.front + lineWidth )

        var nine = square( "9" )
        var ten = square( "10" ).leftTo(nine.right-lineWidth)
        var eleven = square( "11" ).leftTo(ten.right-lineWidth)
        val row3 = (nine + ten + eleven).backTo( row2.front + lineWidth )

        val rows = (row1 + row2 + row3).centerXY()

        val base = Cube( rows.size.x, rows.size.y, baseT+textT ).centerXY()
            .color( "LightGrey" )
        val text = Text( str, BOLD, 36 ).toPolygon().center().extrude( textT + 0.1 )
            .bottomTo( baseT )
            .color( "White" )
        val lines = (Square( base.size.x ).center() - Square( base.size.x - lineWidth*2 ).center())
            .extrude( lineZ )
            .bottomTo( rows.top )
            .color( "DarkGrey" )

        return base - text + rows + lines
    }


    var boxGap = 5.5
    var boxWall = 1.2
    val boxSlots = 9
    val boxExtra = 50

    fun box() : ThickWalledBox {

        return ThickWalledBox().apply {
            width = 63
            bottomHeight = 38
            topHeight = 25
            depth = (boxGap+boxWall) * boxSlots + boxExtra
        }
    }

    @Piece
    fun boxBottomOld() :Shape3d {
        val box : ThickWalledBox = box()
        val bottom = box.bottom().centerXY()

        val tile : Shape3d = tile("3").previewOnly().rotateX(90).bottomTo(box.thickness)
        val bigCoin : Shape3d = bigCoin().previewOnly().rotateX(90)
            .centerZTo(tile.corner.z + tile.size.z/2)
            .backTo(tile.front)

        val walls = Cube( 5, boxWall, bottom.size.z - 15 )
            .rightTo(bottom.right - box.thickness)
            .mirrorX().also()
            .tileY( boxSlots + 1, boxGap )
            .backTo( bottom.back - box.thickness + boxWall )
            .bottomTo( box.thickness )

        val dice = Cube( 17 ).backTo(walls.front).tileX(2, 10).previewOnly().centerX()

        return bottom + walls + dice + tile + bigCoin
    }

    /**
    An improvement? over version 1 of the box. The bigCoins and tiles have their own sections,
    and the small coins and dice all go into the remaining third.
    The box is the same size as version 1, so I can reuse the lid.
    */
    @Piece
    fun boxBottom() :Shape3d {
        val box : ThickWalledBox = box()
        val bottom = box.bottom().centerXY()

        val tile : Shape3d = tile("3").previewOnly().rotateX(90).bottomTo(box.thickness)
        val bigCoin : Shape3d = bigCoin().previewOnly().rotateX(90)
            .bottomTo(box.thickness)


        val bigCoins = bigCoin.tileY(10, 0.2)
            .backTo(bottom.back - box.thickness)

        val divider = Cube( bottom.size.x, 1.2, 35 )
            .centerX().bottomTo(box.chamfer)
            .backTo( bigCoins.front - 2 )

        val tiles = tile.tileY(9, 0.2)
            .backTo(divider.front)

        val divider2 = divider.backTo( tiles.front - 2 )

        val dice = Cube( 17 ).tileX(2, 10).previewOnly()
            .centerX().backTo(divider2.front - 10).bottomTo( box.thickness )

        return bottom + dice + tiles + bigCoins + divider + divider2
    }

    @Piece
    fun boxLid() = box().top()


    var logoT = 0.5
    var logoTextT = 0.6
    var logoWidth = 80

    fun logo( str : String ) : Shape3d {
        // Make the logo bigger than is required for now.
        val text = Text( str, "A Charming Font Expanded", 40 ).toPolygon()
        // The offset should be just big enough to join (most of?) the pieces together
        val back = text.offset(2.0, true).extrude(logoT)
        val both = text.extrude(logoT + logoTextT).color("White") + back

        // Ensure the logo is 80 mm wide        
        return both.scale( logoWidth / both.size.x, logoWidth / both.size.x, 1 )
            
    }

    // The box and tiles are intended to look a little like After Eights,
    @Piece
    fun logoAboutSeven() = logo( "About Seven" )

    @Piece
    fun logoSuper7() = logo( "Super Seven" )



    override fun build() : Shape3d {
        val square : Shape3d = square( "1" )
        val tile = tile3()
        val coin : Shape3d = coin().mirrorZ().bottomTo(baseT+textT+0.1)
        val bigCoin : Shape3d = bigCoin().mirrorZ().bottomTo( tile.top - lineZ + 0.1 ).previewOnly()

        return tile + coin + bigCoin + coin.translate( square.size.x - lineWidth, square.size.x - lineWidth, 0 )
    }

    /*
    override fun postProcess(gcode: GCode) {
        if ( changeFilament ) {
            if ( getPiece().startsWith( "tile" ) || getPiece().startsWith("row") ) {
                pauseAtHeight( gcode, baseT, "Change Filament" )
                pauseAtHeight( gcode, baseT + textT, "Change Filament" )
            }
            if (getPiece().startsWith( "logo" ) ){
                pauseAtHeight( gcode, logoT, "Change Filament" )
            }
        }
    }
    */

}

/*
Rules copied from http://nickthecoder.co.uk:8081/instructions/superSeven.html

Super Seven is a two player game, that works with the same ideas as noughts and crosses, but is much more fun, has more strategy, and also some luck.

The board is made up of 9 boxes, and in each box there are 9 squares. Each of the boxes is labelled 3 to 11. The squares are also labelled 3 to 11, with the exception of the each middle square, which isn't numbered,

The board is initially empty. The first player (blue), throws a pair of dice. The sum of the two dice determines where the player is allowed to place their counter.
Valid Moves

    If the player rolls a 7, then he may place his counter in any unoccupied center squares ( the ones labelled '*' ). Alternatively, he may place his counter in any of the unoccupied squares in the center box (the 7 box).
    If the player rolls 3 to 6, or 8 to 11, the player may place their counter into any unoccupied square with the corresponding number. Alternatively, they may choose to place their counter on any numbered square within the box with the corresponding number. He may not place his counter in any of the center squares ( labelled '*' ).
    For example, if the player rolls a 1 and a 2, (totalling 3), he may place his piece into any square numbered 3 ( in the top left of each box). Alternatively he may place his counter into square 3, 4, 5, 6, 8, 9, 10 or 11 of the top left most box (the 3 box).
    If the player rolls a 2, he may place his counter on any unoccupied square.
    If the player rolls a 12, he may remove any of his opponents counters, and replace it with his own.

Passes

If the player cannot move, then he may throw the dice again. If he still cannot move, he may throw the dice once more. However, if after three throws of the dice, the player can still not make a legal move, then the player must pass.
Aim of the Game

The aim of the game is to make lines of three, just as in the game noughts and crosses. Once a player has a line of three within a box, that box becomes fully controlled by that player - all 9 squares are filled with the player's counters. Neither player may play in this box after it has been filled.

Another way to control a box is to place five counters of the same colour into the box. All 9 squares are then filled with the player's counters.

The winner is the first player to get a line of three boxes, or get control of 5 out of the 9 boxes. 
*/