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)
        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)
                    Dim clicksRemaining = CInt(CallByName(item, "intClicksRemaining", CallType.Get, Nothing))
                    Btns(i).Text = (clicksRemaining).ToString
                    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 Try
                    Dim lightOn = CallByName(item, "boolLightOn", CallType.Get, Nothing)
                    Dim enabled = CBool(lightOn)
                    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 Try
                ' This is for boardArray As Boolean
                Dim enabled = CBool(CObj(boardArray(i)))
                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

        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

        If lostTheGame Then
            For Each btn In Btns
                btn.Enabled = False
            MessageBox.Show("Sorry you ran out of moves.", "You lost", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
        End If
        If wonTheGame Then
            Dim score = 0
            For Each btn In Btns
                btn.Enabled = False
                    Dim clicksRemaining = CInt(CallByName(btn, "intClicksRemaining", CallType.Get, Nothing))
                    score += clicksRemaining
                Catch ex As Exception
                End Try
            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()
        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


  • 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.


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.