# LightsOut - A homework framework for teaching CS to non-engineers

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

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)

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
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)
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)))
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)
Next
Me.Controls.Clear()

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)
End If
End Sub

Sub DiscoParty(btns() As Button)
While (True)
For Each btn In btns
btn.BackColor = getRandomColor()
Next
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.