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. */