Tetrix – Absolute Position Version
CMPE2300 - Lab03 Thursday, November 12, 2020
Lab 03 – Tetrix – Absolute Position Version In this lab you will use collections to create a variation of a classic video game : Tetris.
I won't bore you with the details – you all know the game. Our toolbox full of collections and their related functionality will provide the foundation to build this partially faithful reproduction.
Program Specification
You are required to include the CDrawer assembly to display our game.
The real game is comprised of “Shapes”, but when you break it down, each shape is actually a collection of blocks... hmm, that looks to be a good start. While I will supply the build specification ( in order to exercise the collection content ), please investigate and understand each component and how it ties things together.
Your instructor will address the relative vs absolute positioning between the Shape and its subservient Blocks. The relative relationship requires tight coupling ( the Blocks must be explicitly aware of their parent Shape ), but an absolution positioning relationship de- couples the Block to Shape relationship, instead putting the responsibility on the Shape to keep the Blocks properly herded.
You will adding a few classes to our project. The first call Shape. Within this same file, but below the Shape class, add an additional class called Block. ( these classes are tightly coupled and should reside together )
Your Block class will include :
a) a static CDrawer object member, initialized to null. Include an accessible manual property. The getter should merely return the current CDrawer member, while the setter will Close() an existing CDrawer before assigning the new value.
b) a static automatic property of type Random with a public getter and hidden setter.
c) A Point member representing the Block location, provide an additional manual get only property for this member.
d)a member of type color
Add an instance constructor accepting a Point representing the parent’s origin home coordinate, a Point representing this Block’s offset and a color. Set your origin location based on the parent point, then apply the offset specific to this Block. Set your color.
Add another instance constructor, known as a copy-constructor. It accepts a Block argument, initialize the new instance with members of the argument instance. This is used for collision testing.
Add a public ShowBlock method, no args, no return value. At the blocks location add a block to the CDrawer of size 1 in our current color.
Override Equals. Provide value-semantics where the blocks will be considered equal if
CMPE2300 - Lab03 Thursday, November 12, 2020
they have identical location coordinates. ( complete GetHashCode() too )
Add a public Move method, accepting a Point argument representing the offset to move this block, no return value. Apply the offset to your location.
Add a public Rotate method, accepting a Point representing the parent Shape origin, no return value. This will be used to “rotate” our blocks around our parent Shape class. The process is to negate the offset, moving the block back around the true origin(0,0), the apply the Rotation ( instructor will show how easy this is for blocks ), then apply the offset argument back onto the block to move it back to it’s original location.
Add some predicates – or lambdas may be used, but their behavior will be described by these terms :
CanShiftLeft – true if our block wouldn't go out of bounds on the left
CanShiftRight – true if our block wouldn't go out of bounds on the right
CanFall – true if our block is not at the bottom of the CDrawer ( it will be scaled )
Our blocks need to be combined into Shapes.
Complete your Shape class with the following members :
a) 3 readonly Points representing the offset of Down, Left, and Right
b)a public enumeration of our shape types Square, Line, Angle, ( T optional )
c) a automatic propertiy of our enumeration defining our Shapes type, hide the setter
d)a public automatic property of type bool representing whether the Shape is falling
e)a private member of type Point indicating the Shape core location, also supply a manual property – getter only returning this location
f) a member that is a List of Blocks, use an initializer – these comprise our shape
g)a predicate ( or lambda ) called Falling returning true if the Shape is falling
An instance constructor accepting a Point representing the Shape base location. Set your falling flag to true, pick a random color and assign your member location. Now randomly assign an enumeration type and based on its type you will have to create and add its respective blocks ** See diagram from instructor.
Add another instance constructor – a copy constructor accepting an argument of type Shape and a Point argument representing the offset this new Shape apply. Initialize all member fields to the Shape argument – this will leverage the Block copy constructor to build this new Shape’s Block list. Finally, invoke Move() with the offset to shift the Shape to the desired location.
CMPE2300 - Lab03 Thursday, November 12, 2020
Override Equals() and GetHashCode(). This Equals will use an extension method to determine if any of the argument Shape blocks overlap our own. True means an overlap occurs.
Add a Move() method accepting a Point representing the offset to move by. Apply the Shape location and apply the offset to all Blocks of the shape.
Create a ShowShape() method to show this Shapes Blocks.
Create a Drop method, returning nothing, accepting a List of Block representing the current “floor” ( all Blocks from dropped and stopped Shapes ). This method will be used to “Drop” our Shapes down one step. Follow these steps :
– If you are not falling, return
– if all your Blocks can not fall, the some block must of hit the bottom of the CDrawer. Then - set Falling to false ( for later cleanup ) and add all the blocks to the floor list.
– At this point we need to determine if it is safe to drop – this depends on what is below. Create a temporary test Shape using the copy constructor with the supplied offset argument being “Down”. Check the test Shape’s blocks against the “floor” using an appropriate collection/extension method ( no loops required ).
– If we are overlapped, an obstruction was encountered. We can ignore the test Shape, rather our actual Shape can NOT move, so we set falling to false and add the Shape’s blocks to the floor.
– If we didn't overlap, we have room to drop, invoke Move with “Down”.
Create a ShiftLeft() method, accepting the List of Block as the current floor. Utilizing an extension method, and in a fashion similar to Drop(), shift left if possible. First testing hard boundaries, then checking against floor blocks that may impede the ability to shift.
Create a ShiftRight() method, accepting the List of Block as the current floor. Utilizing an extension method, and in a fashion similar to Drop(), shift right if possible. First testing hard boundaries, then checking against floor blocks that may impede the ability to shift.
Now we must be able to “Play” our base version of this game.
In your main form, you require some members :
a new StopWatch from System.Diagnostics ( this will be our timing element ), and a timer component
a Queue of Shapes, representing the Shapes to complete the current level
a Shape reference, initialized to null, this will be our currently dropping shape
CMPE2300 - Lab03 Thursday, November 12, 2020
a List of Block representing our “dropped and stopped” Shape’s blocks, our Floor
4 bools to hold our key states of up, down, left and right. Up will rotate our Shape.
And Form key down and up handlers, setting the appropriate key state members. For sanity, when the CDrawer is instantiated create a keyboard event handler and integrate both form and CDrawer key events so it won’t matter which window has focus.
In our constructor, set your timer interval to 50ms and enable it ( always running, always ). Also start our stopwatch now.
In our new Game Button handler. Set our initial scale to 50. Then based on the numericupdown value adjust the scale ( if necessary ) to make our game fit in the biggest scale with a max width of 500. With your calculated scales, create your CDrawer with the calculated width and twice that for height. Be sure to then set your scale after creation. Create a new Queue and populate it with NN ( 20 ) new Shapes ( centered at the top ). Clear your current floor.
The worker for this game is the timer handler, following this basic outline :
If there is no game ( no CDrawer ) return, nothing to see here, now
If Either the down key is pressed or NN ( 1000ms ) has elapsed on our watch, its time to drop.
– If our current dropping shape is “done” and we have more shapes in our queue, grab one from the Queue and make it our current dropping shape.
– Drop our Shape
– If our Shape is done, set it to null - it was moved to our Floor, so it just transferred ownership.
– Reset() our stop watch.
Perform your key processing Left/Right/Up/Down.
Now show floor shapes, then show our current shape.
That's it. This provides the basic foundation for the game. You are required to provide a few extra features to complete this lab. You must provide a way for levels to be implemented such when a level completes, a new wave with more Shapes comes your way, with a way to track the number of successfully “dropped” blocks. Your UI should also indicate then next waiting shape – either literally or visually.
CMPE2300 - Lab03 Thursday, November 12, 2020
End of BASE Lab component [ 75% ], Enhancements [ 25% ]
Enhancements include a Dictionary used to provide statistics on the Shape types that have been played, Processing row clear when full ( and possibly drop of blocks if a row clears ).
Assumptions and Particulars
No real assumptions can be made, but ensure your lab has a single Random() object and CDrawer that reside in our Electron class.
You may include additional functionality to make your lab easier to code and test, ensure they don't interfere with specified operation, or can be turned on/off with a properly labeled checkbox.
- Lab 03 – Tetrix – Absolute Position Version
- Program Specification
- End of BASE Lab component [ 75% ], Enhancements [ 25% ]
- Assumptions and Particulars