2
\$\begingroup\$

This provides a fill-in-the-blanks homework framework to implement the game "LightsOut".

LightsOut is a game with a grid of lights. When you touch a light, it toggles it’s state; it ALSO toggles the state of the lights adjacent to it (in a cross pattern). The goal is to turn all the lights off.

In addition to the normal game operation, each cell will have a number of uses/clicks until it is disabled. The goal is not readability/extensibility in the scaffolding section as students will not concern themselves with reading it; rather the focus is on making it hard for students to get lost while they try to learn arrays and custom types.

  • Two distinct parts, solution and scaffolding. Students are supposed to fill in the solution and should have no reason to touch or look at the scaffolding
  • Solution should be able to be found sequentially without forced gaps where code is unable to be run
  • Whole project must be in one file (file management complexity)
  • UI independant (no need to use Visual Studio's UI editor & helps with distribution)
  • Actual solution to the LightsOut game must use arrays and structures
  • Scaffolding should be resilient to simple typos
  • Comments should be clear but not verbose (jargon tends to get tuned out)
  • Scaffolding should not hinder performance to the point of inconvenience
  • Length. While this scaffolding implementation turned out fairly long as more features were added, shorter would be better as it simply distracts less from the assignment
  • Assignment will be paired with a handout, however I'd like the comments to be nearly sufficient

Visual Basic.NET code follows:

Option Strict On
Option Explicit On
Public Class Form1
    ' The board is square so this should be a perfect square number. (it will be rounded down to the nearest perfect square)
    Const sizeOfBoard As Integer = 25
    Dim sizeOfBoardEdge As Integer = CInt(Math.Floor(Math.Sqrt(sizeOfBoard)))


    ' PART 1: Make a custom structure with two variables inside. A Boolean named boolLightOn and an Integer named intClicksRemaining
    ' Put your structure declaration here ↓

    ' Put your structure declaration here ↑
    ' When you make your custom structure, change the type of this array. It should end with:    As YourStructureName
    Private boardArray(sizeOfBoard - 1) As Boolean

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Text = "Lights Out!"
        ' If you want to setup the board (maybe randomize it?) you can do that here
        ' PART 1: Write a loop that makes all the lights start ON (Right now they start off because Booleans are false by default)
        '         and start with 5 clicks remaining

        RedrawBoard() ' This will draw the board for the first time
    End Sub

    Public Sub ButtonGotClicked(ByVal sender As Object, ByVal e As System.EventArgs)
        ' You can use intButtonID to get the number of the button clicked
        Dim intButtonID As Integer = CInt(CType(sender, Button).Tag)
        ' Since each row is sizeOfBoardEdge long, you can divide and round down to find the row number
        Dim intRowCoordinate As Integer = intButtonID \ sizeOfBoardEdge
        ' Since each row is sizeOfBoardEdge long, you can divide and get the remainder to find the column number
        Dim intColumnCoordinate As Integer = intButtonID Mod sizeOfBoardEdge

        ' ============================================================================================================
        ' Your code goes here in this area

        ' PART 1: When you add your custom structure edit the line below to use boolLightOn
        boardArray(intButtonID) = Not boardArray(intButtonID) ' This updates boardArray to change the button you clicked
        ' PART 2: When you click a button, you should subtract 1 from the clicks remaining.

        ' PART 2: When you click a button it should toggle the buttons adjacent to it in a cross shape

        ' You can delete this when you understand what is going on
        MessageBox.Show("You clicked button number: " + Convert.ToString(intButtonID) +
                        " Coordinate: (" + Convert.ToString(intRowCoordinate) + ", " + Convert.ToString(intColumnCoordinate) + ")")

        ' Leave everything below this line alone
        ' ============================================================================================================
        RedrawBoard() ' Now that you have updated boardArray we have to redraw the board
    End Sub

    ' You don't need to look at this function. Just call RedrawBoard() when you need to update the display
    Sub RedrawBoard()
        Me.Size = New Size(sizeOfBoardEdge * 66, sizeOfBoardEdge * 66)
        Me.ClientSize = New Size(sizeOfBoardEdge * 66, sizeOfBoardEdge * 66)
        Dim Btns(sizeOfBoardEdge * sizeOfBoardEdge - 1) As Button
        Dim table As TableLayoutPanel = New TableLayoutPanel
        Dim type = boardArray.GetType()
        Dim stateList As List(Of Boolean) = New List(Of Boolean)
        Dim scoreList As List(Of Integer) = New List(Of Integer)
        Console.WriteLine("Type was: " + type.ToString)
        Console.WriteLine(type.IsValueType)
        Console.WriteLine(type.IsPrimitive)
        Console.WriteLine(type.Namespace.StartsWith("System"))
        Console.WriteLine(type.IsEnum)
        table.Size = Me.ClientSize


        For i As Integer = 0 To Btns.Length - 1
            Btns(i) = New Button
            Btns(i).Anchor = AnchorStyles.Bottom Or AnchorStyles.Left Or AnchorStyles.Right Or AnchorStyles.Top
            If Not type.IsValueType And Not type.IsPrimitive And Not type.Namespace.StartsWith("System") And Not type.IsEnum Then
                ' This is for boardArray As Tile
                Console.WriteLine("Struct: " + (i).ToString)
                Dim item = boardArray(i)
                Try
                    Dim clicksRemaining = CInt(CallByName(item, "intClicksRemaining", CallType.Get, Nothing))
                    Btns(i).Text = (clicksRemaining).ToString
                    scoreList.Add(clicksRemaining)
                    Console.WriteLine("    intClicksRemaining: " + (clicksRemaining).ToString)
                Catch ex As MissingMemberException
                    MessageBox.Show("Your custom struct did not contain a Integer named intClicksRemaining. Array index: " + (i).ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop)
                    End
                End Try
                Try
                    Dim lightOn = CallByName(item, "boolLightOn", CallType.Get, Nothing)
                    Dim enabled = CBool(lightOn)
                    stateList.Add(enabled)
                    Btns(i).ForeColor = If(enabled, Color.Black, Color.White)
                    Btns(i).BackColor = If(enabled, Color.White, Color.Black)
                    Console.WriteLine("    boolLightOn: " + (enabled).ToString)
                Catch ex As MissingMemberException
                    MessageBox.Show("Your custom struct did not contain a Boolean named boolLightOn. Array index: " + (i).ToString, "Error", MessageBoxButtons.OK, MessageBoxIcon.Stop)
                    End
                End Try
            Else
                ' This is for boardArray As Boolean
                Dim enabled = CBool(CObj(boardArray(i)))
                stateList.Add(enabled)
                scoreList.Add(-1)
                Btns(i).BackColor = If(enabled, Color.White, Color.Black)
            End If
            Btns(i).Tag = i
            Btns(i).Size = New Size(60, 60)
            table.Controls.Add(Btns(i), i Mod sizeOfBoardEdge, i \ sizeOfBoardEdge)
            AddHandler Btns(i).Click, AddressOf ButtonGotClicked
        Next
        Me.Controls.Clear()
        Me.Controls.Add(table)

        Dim wonTheGame As Boolean = Not stateList.Contains(True)
        Dim lostTheGame As Boolean = True
        For Each x In scoreList
            If x <> 0 Then
                lostTheGame = False
                Exit For
            End If
        Next

        If lostTheGame Then
            For Each btn In Btns
                btn.Enabled = False
            Next
            MessageBox.Show("Sorry you ran out of moves.", "You lost", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            End
        End If
        If wonTheGame Then
            Dim score = 0
            For Each btn In Btns
                btn.Enabled = False
                Try
                    Dim clicksRemaining = CInt(CallByName(btn, "intClicksRemaining", CallType.Get, Nothing))
                    score += clicksRemaining
                Catch ex As Exception
                End Try
            Next
            MessageBox.Show("WINNER: Your score was: " + score.ToString, "You won", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            Task.Run(Sub() DiscoParty(Btns))
        End If
    End Sub

    Sub DiscoParty(btns() As Button)
        While (True)
            For Each btn In btns
                btn.BackColor = getRandomColor()
            Next
            Threading.Thread.Sleep(200)
        End While
    End Sub

    Function getRandomColor() As Color
        Return Color.FromArgb(getRandomColorChannel(), getRandomColorChannel(), getRandomColorChannel())
    End Function

    ' Generate Random number in range 0 ... 255
    ' https://msdn.microsoft.com/en-us/library/f7s023d2(v=vs.90).aspx
    Function getRandomColorChannel() As Integer
        Const upperbound = 255
        Const lowerbound = 0
        Return CInt(Math.Floor((upperbound - lowerbound + 1) * Rnd())) + lowerbound
    End Function
End Class

Questions:

  • Does the solution fit the requirements?
  • Where can this be improved to provide a better experience to a non-savvy intro student?
  • Where can the performance be improved? Some of the students run in VMs (as do I) and some run on extremely outdated hardware.
  • Win/lose checking code definitely smells, cleaner solution?
  • Can any of the comments be reworded to be clearer?
  • (minor) General suggestions to naming, design, and refactoring?

Obviously, please do not solve the actual homework.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.