I have written a Sudoku app for Ruby. This is my first attempt at coding in Ruby so there are sure to be lots of way to optimise it or make it more efficient.To summarise the code: it takes an input file of a Sudoku problem in the format comma separated row, column, value of the initial Sudoku problem.This is stored in an array and a hash. The user can then add or delete numbers with the function adddelete(array,hash) and the program checks whether a repeated number occurs in any box, row or column row with rowcheck(array), colcheck(array) and boxcheck(array). If repeated numbers are found it prints an error message. The game is complete when the Sudoku grid is filled - checkwin(array) with no errors - rowcheck(array), colcheck(array) and boxcheck(array).Any suggestions on how to improve this would be appreciated!Some specific questions as no answers yet: What is good practice to test this code once it is written? Profiling? Unit tests? How would I write unit tests for this - just a bunch of Sudoku problems and see if they parse correctly, or would unit tests only be required for something a bit more complicated e.g. a Sudoku solver?def printarray(array)    puts(_________________________)    puts(|  + array[0] +   + array[1] +   + array[2] +  |  + array[3] +   + array[4] +   + array[5] +  |  + array[6] +   +array[7] +   + array[8]  +  | )    puts(|  + array[9] +   + array[10] +   + array[11] +  |  + array[12] +   + array[13] +   + array[14] +  |  + array[15] +   + array[16] +   + array[17] +  | )    puts(|  + array[18] +   + array[19] +   + array[20] +  |  + array[21] +   + array[22] +   + array[23] +  |  + array[24] +   + array[25] +   + array[26] +  | )    puts(|-----------------------|)    puts(|  + array[27] +   + array[28] +   + array[29] +  |  + array[30] +   + array[31] +   + array[32] +  |  + array[33] +   + array[34] +   + array[35] +  | )    puts(|  + array[36] +   + array[37] +   + array[38] +  |  + array[39] +   + array[40] +   + array[41] +  |  + array[42] +   + array[43] +   + array[44] +  | )    puts(|  + array[45] +   + array[46] +   + array[47] +  |  + array[48] +   + array[49] +   + array[50] +  |  + array[51] +   + array[52] +   + array[53] +  | )    puts(|-----------------------|)    puts(|  + array[54] +   + array[55] +   + array[56] +  |  + array[57] +   + array[58] +   + array[59] +  |  + array[60] +   + array[61] +   + array[62] +  | )    puts(|  + array[63] +   + array[64] +   + array[65] +  |  + array[66] +   + array[67] +   + array[68] +  |  + array[69] +   + array[70] +   + array[71] +  | )    puts(|  + array[72] +   + array[73] +   + array[74] +  |  + array[75] +   + array[76] +   + array[77] +  |  + array[78] +   + array[79] +   + array[80] +  | )    puts(*************************)    puts(\n)    return 0enddef adddelete(array,h)# dialogue for adding/deleting numbers from sudoku matrix    puts(add/delete? add = 1, delete = 2\n?)    add = gets.chomp    if add == 1        puts (row (1-9)?)        row = gets.chomp        if (row.to_i < 1 or row.to_i > 9) then            puts (invalid number try again)            return 0        end        puts (column (1-9)?)        column = gets.chomp        if (column.to_i < 1 or column.to_i > 9) then            puts (invalid number try again)            return 0        end        puts (number (1-9)?)        number = gets.chomp        if (number.to_i < 1 or number.to_i > 9) then            puts (invalid number try again)            return 0        end        row = row.to_i        column = column.to_i        array.fill(number,(row-1)*9+column-1,1)        h.fill(number,2*((row-1)*9+column)-1,1)        hash = Hash[*h]    elsif add == 2        puts (row (1-9)?)        row = gets.chomp        if row.to_i < 1 or row.to_i > 9 then            puts (invalid number try again)            return 0        end        puts (column (1-9)?)        column = gets.chomp        if column.to_i < 1 or column.to_i > 9 then            puts (invalid number try again)            return 0        end        puts (confirm delete y/n?)        delete = gets.chomp        if delete == y then            row = row.to_i            column = column.to_i            array.fill( ,(row-1)*9+column-1,1)            h.fill(x,2*((row-1)*9+column)-1,1)            hash = Hash[*h]        else            return 0        end    else        puts (invalid number try again)        return 0    end    return 0enddef rowcheck(array)    $rowfail = false    $i = 0    $num = 8#check each row for repeated numbers    begin        numbers = array[($i)*9..($i+1)*9-1]        counts = Hash.new 0        numbers.each do |number|            counts[number] += 1        end        row = $i + 1        numbers.uniq.each do |number|           if counts[number].to_i > 1 and number !=   then               puts repeated number  + number.to_s +  in row  + row.to_s + \n + \n               $rowfail = true           end        end        $i += 1    end while $i <= $num    if $rowfail == false        return 0    else         return 1    end    enddef colcheck(array)#check each column for repeated numbers    $colfail = false    $j = 0    $num = 8     begin        numbers = []        numbers << array[$j+0]        numbers << array[$j+9]        numbers << array[$j+18]        numbers << array[$j+27]        numbers << array[$j+36]        numbers << array[$j+45]        numbers << array[$j+54]        numbers << array[$j+72]        numbers << array[$j+63]       # puts numbers        counts = Hash.new 0        numbers.each do |number|            counts[number] += 1        end        column = $j + 1        numbers.uniq.each do |number|           if counts[number] > 1 and number !=   then               puts repeated number  + number.to_s + in column + column.to_s + \n + \n               $colfail = true           end        end        $j += 1    end while $j <= $num    if $colfail == false        return 0    else        return 1    end    enddef boxcheck(array)    $boxfail = false    $k = 1    $num = 9    begin#check each box        if $k/3.0 <= 1.0 then            numbers = []            numbers << array[($k-1)*3]            numbers << array[($k-1)*3+1]            numbers << array[($k-1)*3+2]            numbers << array[($k-1)*3+9]            numbers << array[($k-1)*3+10]            numbers << array[($k-1)*3+11]            numbers << array[($k-1)*3+18]            numbers << array[($k-1)*3+19]            numbers << array[($k-1)*3+20]        end        if $k/3.0 > 1.0 and $k/3.0 <= 2.0 then            numbers = []            numbers << array[($k-1)*6+3*(3-(($k-1)-3))]            numbers << array[($k-1)*6+3*(3-(($k-1)-3))+1]            numbers << array[($k-1)*6+3*(3-(($k-1)-3))+2]            numbers << array[($k-1)*8+5*(3-($k-1-3))-3]            numbers << array[($k-1)*8+5*(3-($k-1-3))-2]            numbers << array[($k-1)*8+5*(3-($k-1-3))-1]            numbers << array[($k-1)*9+6*(3-($k-1-3))]            numbers << array[($k-1)*9+6*(3-($k-1-3))+1]            numbers << array[($k-1)*9+6*(3-($k-1-3))+2]        end        if $k/3.0 > 2.0 and $k/3.0 <= 3.0 then            numbers = []            numbers << array[($k-1)*7+4*(9-(($k-1)))]            numbers << array[($k-1)*7+4*(9-(($k-1)))+1]            numbers << array[($k-1)*7+4*(9-(($k-1)))+2]            numbers << array[($k-1)*8+5*(9-(($k-1)))]            numbers << array[($k-1)*8+5*(9-(($k-1)))+1]            numbers << array[($k-1)*8+5*(9-(($k-1)))+2]            numbers << array[($k-1)*9+6*(9-(($k-1)))]            numbers << array[($k-1)*9+6*(9-(($k-1)))+1]            numbers << array[($k-1)*9+6*(9-(($k-1)))+2]        end        counts = Hash.new 0        numbers.each do |number|            counts[number] += 1        end        numbers.uniq.each do |number|            if counts[number] > 1 and number !=   then                puts repeated number  + number.to_s +  in box + $k.to_s + \n + \n                $boxfail = true            end        end        $k+=1    end while $k<=$num    if $boxfail == true        return 1    else        return 0    end    enddef checkwin(array)    $i = 0    $num = 80    $check = true    array.each do |number|        if number ==   then            $check = false        end    end    return $checkend# create array to store sudoku numbers and h to store sudoku numbers # h to be converted to hasharray = Array.new(81)h = Array.new(162)array.fill( )h.fill(x)# open input fileFile.open(/home/squirrel/coding/easy3.sudoku, r) do |f|# fill in numbers$i=0$num = 81f.each_line do |line|    values = line.split(,)    row = values[0].to_i    column = values[1].to_i    number = values[2].to_s.chomp    array.fill(number,(row-1)*9+column-1,1)    h.fill(number,2*((row-1)*9+column)-1,1)end# fill in hash keyswhile $i < $num    h.fill($i,2*$i,1)    $i += 1end# create hashhash = Hash[*h]$FAIL = TRUEprintarray(array)begin    adddelete(array,h)     printarray(array)    if (rowcheck(array) + colcheck(array) + boxcheck(array) == 0) and checkwin(array)==true then         $FAIL = FALSE    endend while $FAIL == TRUEputs (Congratulations! You win)endA sample input file is below:1,2,61,4,11,6,41,8,52,3,82,4,32,6,52,7,63,1,23,9,14,1,84,4,44,6,74,9,65,3,65,7,36,1,76,4,96,6,16,9,47,1,57,9,28,3,78,4,28,6,68,7,99,2,49,4,59,6,89,8,7
Sudoku app for Ruby
beginner;performance;ruby;sudoku
Global variablesThe code has a fair number of global variables such as $i whichcould possibly be local variables (simply i).  Use a local variablewhenever the value does not need to be shared among methods.  When itdoes need to be shared, prefer a member variable (@i).  Globalvariables are a source of friction in testing, code reuse, andrefactoring.Using method names as commentsGood job using comments to help the reader understand the why behindthe code.  However, a well named method is the best kind of comment.When you think of writing a comment, consider whether the code you arecommenting coul dbe placed into a separate method, with the name ofthat method conveying the same information as the comment.  Not allcomments can be removed this way, but many can, and the code willcommunicate its intent better.Use underscores in identifiersWhen a method or variable name in Ruby is composed of multiple words,separate them with underscores, both because it is the commonconvention in Ruby, and because it is easier to read.  For example,add_delete is preferable to adddelete.Make each method do exactly one thing.Make each method do one thing when possible.  This eases the pain offinding good names, and also makes the code easier to understand.The method #adddelete is doing three things:Reading the user's moveChecking that the move is validApplying that move to the game boardIt may be possible that this function would be made easier to read ifit delegated most of the work to other functions that each did justone thing.Consider using two-dimensional arraysA two-dimensional array is a more natural data structure forrepresenting a two-dimensional playing board.  For example, anine-by-nine array initialized with spaces can be constructed likethis:Array.new(9) { Array.new(9) { } }Case of true/falseThe code surprised me with the use of all-caps Boolean literals:$FAIL = FALSEI didn't know Ruby had that!  However, the literals true and falseshould be preferred.Testing for truthinessConditions should rarely test for equivalance with a Boolean literal.This code:while $FAIL == TRUEWould be better as:while !$FAILBut consider using until to remove the need for the negation:until $FAILPrefer loops with the condition at the topIt often reads easier to put the loop condition at the top of theloop:until $FAIL  ...endThe syntax is easier to read.  Also, when the loop condition is at thebottom, Ruby will execute the loop body at least once.  That might bewhat you need, and in fact the reader of your code will expect thatthat's why you put the condition at the bottom.