Demo entry 6352883

something

   

Submitted by taylor ring on Mar 27, 2017 at 21:48
Language: Python 3. Code size: 73.6 kB.

import tkinter as tk
from tkinter import messagebox
import random

#this class will create cell objects. these will contain all of the information that
#repesent the cell. the variables and functions within the class is what is required at for this to work.
#as the testing is continued, additional variables might need to be impplemented for a function to work, and to keep it organized.
class CellCreation():
    def __init__(self,row,column,deliveryCost): # this is just entering data from the outside into a object
        self.row = row
        self.column = column
        self.cost = deliveryCost
        self.stock = 0
        self.II = 0
        self.EC = False
        self.change = ""
        self.DNU = False
        self.empty = True
        self.Nstock = 0

    def info(self): # this is created so information can be seen. this is to make sure the program is working correctly.
        print("row: "+str(self.row))
        print("column: "+ str(self.column))
        print("cost: "+str(self.cost))
        print("stock: "+str(self.stock))
        #print("new stock: "+str(self.Nstock))
        print("improvement indices: " +str(self.II))
        #print("degenerate: "+str(self.degenerate))
        print("empty: "+str(self.empty))
        print(" ")

    def SSinfo(self): # this is to grab information related to the stepping stone method. this is just to make sure the program is working correctly when debugging
        print("row: "+str(self.row))
        print("column: "+ str(self.column))
        print("stock: "+str(self.stock))
        print("entering cell: "+str(self.EC))
        print("change: "+self.change)
        print("do not use: ",str(self.DNU))
        print("empty: "+str(self.empty))
        print("New stock: "+str(self.Nstock))
        print(" ")

    def ApplyChange(self,value,cellDone): # this applies the changes that has been declared in the stepping stone method to the stock.
        if self.change == "+":
            self.Nstock = self.stock + value
        elif self.change == "-":
            if self.stock == value and cellDone == False: # this decides if the cell is "empty" or truly empty depending on cellDone and the value of the stock
                self.empty = True
                cellDone = True

            self.Nstock = self.stock - value
        return cellDone

    def resetCell(self): # this 
        if self.change != "":
            self.stock = self.Nstock
        self.EC = False # once the stepping stone method is done, this means that all of the methods for one cycle has been done, so this resets the some of the values to what they were before
        self.change = ""
        self.DNU = False
        self.II = 0
        self.SSinfo()

# using a class to hold the GUI for the user is better, as information is trnasferred easier across different windows.
#it is also a better organsier, for example the quits,helps and warnings.
class Application():
    def __init__(self):
        self.MainWindow()

    def MainWindow(self): # the windows will be in seperate functions so that they can be created again.
        # this is the widgets for the main window. where the user is putting the rows and columns.

        self.root = tk.Tk()
        gridFrames = []
        for i in range(3):
            gridFrames.append(tk.Frame(self.root))
        self.rows = tk.StringVar()
        self.columns = tk.StringVar()
        self.rows.set("3")
        self.columns.set("3")
        #lambda is used so that information can be transferred while also doing the function.
        Bquit = tk.Button(gridFrames[0], text = "quit", command = lambda: self.quits(1))
        #Bcreate = tk.Button(self.root, text = "create grid", command = lambda: self.check(1))
        Brandom = tk.Button(gridFrames[0], text = "create grid", command = lambda: self.RGridWindow(gridFrames,1))
        Babout = tk.Button(gridFrames[0],text = "about", command = self.about)
        Lwelcome = tk.Label(gridFrames[0], text = """welcome to the transportation
problem solver!""")
        Lrows = tk.Label(gridFrames[0],text = "rows(suppliers) between 3 to 10")
        Lcolumns = tk.Label(gridFrames[0],text = "columns(customers) between 3 to 10")
        Erows = tk.Entry(gridFrames[0], textvariable = self.rows)
        Ecolumns  = tk.Entry(gridFrames[0], textvariable = self.columns)
        #the pack placement manager has been used as the interface is not geometricly important right now.
        #this means that we can place them in order and be done.

        Lwelcome.pack()
        Lrows.pack()
        Erows.pack()
        Lcolumns.pack()
        Ecolumns.pack()
        Bquit.pack(side = "left")
        Babout.pack(side = "left")
        Brandom.pack(side = "left")

        CheckVar1 = tk.IntVar(0)
        self.Oresult = [CheckVar1]
        self.RGridWindow(gridFrames,0)
        
        for i in range(len(gridFrames)):
            gridFrames[i].grid(row = 0, column = i)

        self.root.mainloop() # this signals that there is no more widgets for the window and can finally construct the window.

    # the "quits","helps" functions that uses a system so that different buttons can call different events from the same function.
    #this makes it more easier and quicker to place in new commands for the buttons to call. it also organise the code as well.
    def quits(self,which):# the quit function will close the window that has been called
        if which == 1:
            self.root.destroy()
        elif which == 3:
            self.Copt.destroy()
        elif which == 5:
            self.Show.destroy()
        elif which == 7:
            self.root.destroy()
        elif which == 8:
            self.root.withdraw()
        elif which == 9:
            self.Show.destroy()
            self.root.deiconify()

    def about(self): # this is basic info about what the program is.
        tk.messagebox.showinfo("about","""this is the transportation problem solver that uses the north-west corner method and finds the optimal solution using other methods as well.

by: Taylor Ring
version: 10.5.4 """)

    # this checks if the values entered by the user can be used for the solver. 
    def check(self):
        #self.CgridWindow()
        try:
            x = int(self.rows.get()) # if an error occurs when changing the text into intergers, then a warning will pop up saying that the values were not "numbers".
            #print(x)
            y = int(self.columns.get())
            #print(y)
            if (x >= 3 and x <= 10) and (y >= 3 and y<= 10): # this checks if the numbers are in range, if not a error is rasied.
                return True 

            self.wrong.set("not in range")
            raise ValueError("not in boundries")# a error is manually raised if the values are not in range. without this, the try...except will classify the numbers as "fine" which are noot in this case.
        except:
            self.wrong.set("something's wrong!")
            return False

    def transfer(self,EntryPlace,frames,case): # this transfer the data recieved in the entry boxes into array that has organised information.
        EntryValues = []
        self.deliveryCosts = []
        self.supply = []
        self.demand = []
        rowStore = []
        try:
            area = [int(self.rows.get()),int(self.columns.get())]
        except:
            self.wrong.set("invalid grid size")
            return ""
        #print(self.columns.get())
        #print(len(self.columns.get()))
        
        for i in range(len(EntryPlace)): # this recieves the data from the entry boxes
            EntryValues.append(frames[EntryPlace[i]].get())
        print(EntryValues)
        #print(self.columns.get())

        if (area[0]+1)*(area[1]+1) - 1 != len(EntryValues):
            self.wrong.set("please update grid")
            return ""
        for number in EntryValues:
            if number == "":
                self.wrong.set("missing values")
                return ""
            if number.isalpha() == True:
                self.wrong.set("only whole numbers allowed")
                return ""
            if int(number)<= 0:
                self.wrong.set("no negatives")
                return ""

        checked = True
        if checked == True: # if the values have passed the test, then the function is continued.

            for i in range(len(EntryValues)):
                EntryValues[i] = int(EntryValues[i])

            values = EntryValues.__iter__()
            
            for a in range(area[0]):
                for b in range(area[1]):
                    rowStore.append(values.__next__())
                self.supply.append(values.__next__())
                self.deliveryCosts.append(rowStore)
                rowStore = []
                
            for i in range(area[1]):
                self.demand.append(values.__next__())

            #print("supply")
            #print(self.supply)
            #print("deliveryCosts")
            #print(self.deliveryCosts)
            #print("demand")
            #print(self.demand)

            if sum(self.supply) > sum(self.demand):
                self.dummy = True
            elif sum(self.supply) < sum(self.demand): # its impossible to answer the question if the total supply is less than the total demand, or extra stock is needed.
                self.wrong.set("more demand than supply")
                return ""
            else:
                self.dummy = False

            if case == 0:
                self.quits(4)
            else:
                self.quits(8)
            self.ShowWindow(0)

    def RGridWindow(self,gridFrames,case):
        use = False
        use = self.check()
        if use != True:
            return ""
        if case == 1:
            self.wrong.set("")
            gridFrames[-2].grid_remove()
            gridFrames[-2] = tk.Frame(self.root)
        
        counter = 0
        namesRow = []# the name rows and columns  contain the text to displays to tell the user which is which.
        namesColumn = []
        EntryPlace = [] # records the position number of the cells.
        #EntryValues = []

        for i in range(int(self.rows.get())):
            namesRow.append("S"+str(i+1))
        namesRow.append("DEMAND")

        for i in range(int(self.columns.get())):
            namesColumn.append("D"+str(i+1))
        namesColumn.append("SUPPLY")

        frames = []
        for a in range((int(self.rows.get())*2+3) * (int(self.columns.get())*2+3)):
            frames.append("")

        nameCounter = 0
        for i in range(1,int(self.columns.get())*2 + 2,2):# this starts at the second array data and pplaces the text and lines for the demand line.
            frames[i] = tk.Label(gridFrames[-2],text = "|")
            frames[i+1] = tk.Label(gridFrames[-2],text = namesColumn[nameCounter])
            nameCounter += 1
            #print(nameCounter)

        for a in range(1,int(self.rows.get())*2 + 3,2): # this jumps every other group of frames to create lines, which organizes what the data is connect to.
            for b in range(int(self.columns.get())*2 +3):
                frames[a*(int(self.columns.get())*2+3)+b] = tk.Label(gridFrames[-2],text = "-")###
                #frames[a*(int(self.columns.get())*2+3)+b+1] = tk.Label(frames[a*(int(self.columns.get())*2+3)+b+1],text = "+")###

        for a in range(1,int(self.rows.get())*2+3,2):
            for b in range(int(self.columns.get())*2+3):
                if b % 2 == 0:
                    frames[a*(int(self.columns.get())*2+3)+b] = tk.Canvas(gridFrames[-2],width = 60, height = 10)#tk.Label(self.Cgrid,text = "-")####
                    frames[a*(int(self.columns.get())*2+3)+b].create_line(0,5,60,5)
                else:
                    frames[a*(int(self.columns.get())*2+3)+b] = tk.Canvas(gridFrames[-2],width = 5, height = 10)
                    frames[a*(int(self.columns.get())*2+3)+b].create_line(0,5,5,5)

        nameCounter = 0
        #print(namesRow)
        for a in range(2,int(self.rows.get())*2+3,2): # this uses the frames between the horizontal lines and starts the placements of widgets.
            frames[a*(int(self.columns.get())*2+3)] = tk.Label(gridFrames[-2],text = namesRow[nameCounter])### # this just places the name of the row.
            nameCounter += 1
            #print("durian")
            #print(nameCounter)
            for b in range(1,int(self.columns.get())*2+3 - 1,2): # this creates the widgets that places the entry boxes and the vertical lines.
                frames[a*(int(self.columns.get())*2+3)+b] = tk.Label(gridFrames[-2],text = "|")###
                frames[a*(int(self.columns.get())*2+3)+b+1] = tk.Entry(gridFrames[-2],width = 10)###
                EntryPlace.append(a*(int(self.columns.get())*2+3)+b+1) # the entry boxes are placed in the frames array, but their position is placed in the EntryPlace array
        EntryPlace.pop()          
        frames[0] = tk.Label(gridFrames[-2], text = "X")
        frames[-1] = tk.Label(gridFrames[-2],text = "")

        counter = 0
        #print("feijoa")
        print(frames)
        for a in range(int(self.rows.get())*2 + 3): # this places the widgets on to the window and making it visible
            for b in range(int(self.columns.get())*2 + 3):
                frames[counter].grid(row = a, column = b)
                counter += 1

        # the range of the stocks. this group of code places the entry boxes for the range of stock.
        label = tk.Label(gridFrames[-1],text = "stock range:")
        label.grid(row = 0,column = 0, sticky = "W")
        EMiSRange = tk.Entry(gridFrames[-1],width = 10)# Entry Min Stock Range
        EMiSRange.insert(0,"10")
        EMiSRange.grid(row = 1, column = 0)
        label = tk.Label(gridFrames[-1],text = "to")
        label.grid(row = 1,column = 1)
        EMaSRange = tk.Entry(gridFrames[-1], width = 10) # Entry Max Stock Range
        EMaSRange.insert(0,"20")
        EMaSRange.grid(row = 1, column  = 2)

        #the range of the delivery costs. same as the ranges of stock but instead the range of the deliverycosts.
        label = tk.Label(gridFrames[-1],text = "delivery cost range:")
        label.grid(row = 2, column = 0, sticky = "W")
        EMiDRange = tk.Entry(gridFrames[-1],width = 10)# Entry Min Delivery Range
        EMiDRange.insert(0,"30")
        EMiDRange.grid(row = 3,column = 0)
        label = tk.Label(gridFrames[-1],text = "to")
        label.grid(row = 3, column = 1)
        EMaDRange = tk.Entry(gridFrames[-1],width = 10) # Entry Max Delivery Range
        EMaDRange.insert(0,"50")
        EMaDRange.grid(row = 3, column = 2)


        ranges = [EMiSRange,EMaSRange,EMiDRange,EMaDRange] # this organizes the data to make it easier to transfer functions.
        # other options. for example, the problem can be degenerate and/or have a dummy depending on the user.
        var1 = tk.IntVar()
        var2 = tk.IntVar() # this is nessearry to have for the tick boxes to work.
        self.wrong = tk.StringVar()
        options = [var1,var2]
        self.wrong.set("")
        degen = tk.Checkbutton(gridFrames[-1],text = "have degenerate?",variable = var1) # this creates a tick button.
        dum = tk.Checkbutton(gridFrames[-1],text = "have Dummy?",variable = var2)
        degen.grid(row = 4,column = 0)
        dum.grid(row = 5,column = 0)

        # finalize. this places the buttons to activate different functions.
        BRandom = tk.Button(gridFrames[-1],text = "randomise!",command = lambda: self.randomize(options,ranges,EntryPlace,frames)) # this creates a randomised result.
        BRandom.grid(row = 4,column = 2)
        BSolve = tk.Button(gridFrames[-1],text = "solve!",command = lambda: self.transfer(EntryPlace,frames,1)) # this solves the problem that is in the entry boxes.
        BSolve.grid(row = 5,column = 2)
        #Breturn = tk.Button(gridFrames[-1],text = "quit",command = lambda: self.quits(7)) # this returns the user back to the main window.
        #Breturn.grid(row = 6,column = 0)
        Lwrong = tk.Label(gridFrames[-1],textvariable = self.wrong) # this is used to display a problem with the user's request. this is more quicker to display than a messagw box.
        Lwrong.grid(row = 7,column = 2)
        Bclear = tk.Button(gridFrames[-1],text = "clear",command = lambda: self.clear(EntryPlace,frames))
        Bclear.grid(row = 6,column = 2)
        Boptions = tk.Button(gridFrames[-1],text = "options",command = self.Coptions)
        Boptions.grid(row = 6,column = 0)
        if case == 1:
            gridFrames[-2].grid(row = 0, column = 1)
            
    def clear(self,EntryPlace,Rframes):
        for i in range(len(EntryPlace)): # this takes each entry values, find that entry and make it empty.
            Rframes[EntryPlace[i]].delete(0,"end")

    def randomize(self,options,ranges,EntryPlace,Rframes): # this creates a randomised solution for the user.
        #print(options[0].get(),options[1].get())
        #print(ranges[0].get(),ranges[1].get(),ranges[2].get(),ranges[3].get())
        #print(options)
        #print(ranges)
        #print(EntryPlace)
        demand = []
        supply = []
        cells = []

        try:
            for a in range(int(self.rows.get())):
                for b in range(int(self.columns.get())): # this seperates the and finds the entry boxes of the demand, supply, and delivery cost cells.
                    cells.append(EntryPlace[(int(self.columns.get())+1)*a+b])
                supply.append(EntryPlace[(int(self.columns.get())+1)*a+b+1])
            for i in range(int(self.columns.get())):
                demand.append(EntryPlace[(int(self.columns.get())+1)*a+b+2+i])
        except:
            self.wrong.set("please update grid")
            return ""

        try: # this try and except will check of the numbers are: 1) have no zeros to cause a runtime error, 2) actually have numbers.
            for i in range(len(ranges)):
                if int(ranges[i].get()) == 0: # this automatically checks if the string can be converted into a integer.
                    self.wrong.set("no zero's allowed!") # this creates text and displays it to the user. the function also stops as well.
                    return ""
                elif int(ranges[i].get()) < 0:
                    self.wrong.set("no negatives!")
                    return ""
        except:
            self.wrong.set("they're not whole numbers!") # if changing the string into a teger causes an error, the text will change to display this.
            return ""
        if int(ranges[0].get()) > int(ranges[1].get()): # in case of the user typing the numbers in the wrong order, this sorts out the order and corrects it.
            temp = ranges[0].get()
            ranges[0].delete(0,"end")
            ranges[0].insert(0,ranges[1].get())
            ranges[1].delete(0,"end")
            ranges[1].insert(0,temp)
        if int(ranges[2].get()) > int(ranges[3].get()): # same case for the ranges of delivery costs
            temp = ranges[2].get()
            ranges[2].delete(0,"end")
            ranges[2].insert(0,ranges[3].get())
            ranges[3].delete(0,"end")
            ranges[3].insert(0,temp)


        self.wrong.set("") # if the program got up to here, this means that the results can be used, and the messages dissappears.
        total = random.randint(int(ranges[0].get()),int(ranges[1].get())) # this gets a random number for the total depending on the user.
        supplyValues = [] # this will hold the values of the random numbers.
        demandValues = []
        for i in range(len(supply)):
            supplyValues.append(1) # to make sure that the numbers are not zero, all stock and demands begin with zero
        for i in range(len(demand)):
            demandValues.append(1)
        dummyTotal = total+random.randint(1,total) # the dummy total is the total when the dummy option is selected. 
        while(1): # this gets the values for the supply
            supplyValues[random.randint(0,len(supply)-1)] += 1 # if there are three supply slots, then there is a 1/3 chance that a value in a specific position is increased.
            if options[1].get() == 1: # if the option is used, then the dummy total will be used to compare.
                if sum(supplyValues) == dummyTotal: # if the sum of supplyValues equal to the correct total, then the while breaks.
                    break
            else:
                if sum(supplyValues) == total:
                    break
        #print("total: ",total)
        #print("supply: ",supplyValues)
        passed = False
        while(passed == False): # this gets the values for the demand
            demandValues[random.randint(0,len(demand)-1)] += 1 # this is the same as the supplyValues, but more checks to get the correct solution.
            if sum(demandValues) == total:
                if options[0].get() == 0:
                    #print(demandValues)
                    for i in range(len(demandValues)-1):
                        #print(sum(demandValues[0:i+1]))
                        if sum(demandValues[0:i+1]) != sum(supplyValues[0:i+1]): # a degenerate problem exists if the total numbers before it in supply and demand equals the same.
                            passed = True
                        else:
                            print("old demand: ",demandValues)
                            demandValues[0] -= 1
                            demandValues[-1] += 1
                            print("new demand: ",demandValues)
                            passed = True
                            break
                    #print(passed)
                else: # if the user wants it to be degenerate this will happen instead.
                    if demandValues[0] == supplyValues[0]: # this checks if the current solution will cause a degenerate solution.
                        passed = True
                    else: # if not, this code will interact with the values to guarantee that the problem is degenerate.
                        #if supplyValues[0]+2 >= total: # this checks if the the first value of supply +2 is above the "total", if not, then the values are back to one.
                        excess = supplyValues[0] - demandValues[0] # if so, then the first number will be forced to be the same on both supply and demand.
                        print("old demand: ",demandValues)
                        print("excess: ",excess)
                        supplyValues[0] = demandValues[0]
                        supplyValues[random.randint(1,len(supply)-1)] += excess
                        passed = True

        for i in range(len(supply)): # once the values have been found, they are inserted back into the entry boxes for the user to see before solving.
            Rframes[supply[i]].delete(0,"end")
            Rframes[supply[i]].insert(0,supplyValues[i])
        for i in range(len(demand)):
            Rframes[demand[i]].delete(0,"end")
            Rframes[demand[i]].insert(0,demandValues[i])
        for i in range(len(cells)): # this is very easy to do as the values don't depend on supply or demand.
            Rframes[cells[i]].delete(0,"end")
            Rframes[cells[i]].insert(0,random.randint(int(ranges[2].get()),int(ranges[3].get())))

        return Rframes
            
    def Coptions(self):
        print("activated!")
        options = []
        self.Copt = tk.Toplevel()
        CheckVar1 = tk.IntVar(0)
        #CheckVar1.set(0)
        options.append(tk.Checkbutton(self.Copt,text = "corner view?", variable = CheckVar1))
        if self.Oresult[0].get() == 1:
            options[-1].select()

        Bquit = tk.Button(self.Copt,text = "ok",command = lambda: self.quits(3))

        self.Oresult = [CheckVar1]
        
        for i in range(len(options)):
            options[i].pack()
            
        Bquit.pack()
        self.Copt.mainloop()

    def ShowWindow(self,case): # 
        self.Show = tk.Toplevel()

        #self.MathSolver()
        gridFrames = []

        if self.dummy == True and case == 0: # this will check if the total sum of supply is equal to the total sum of demand.
            print("yes im here!")
            
            for i in range(len(self.deliveryCosts)): # this just inputs the dummy column that is needed. as the deliveryCosts is organised by
                self.deliveryCosts[i].append(0)      # rows, this makes it easier to place cells of 0 cost
            self.demand.append(sum(self.supply)- sum(self.demand)) # this makes sure that the total supply equals the total demand.

        print(self.demand)

        counter = 0
        namesRow = []# the name rows and columns  contain the text to displays to tell the user which is which.
        namesColumn = []
        namesValues = []
        self.changingVSolution = []
        gridNames = ["original question","current solution"]
        #EntryPlace = []
        #EntryValues = []

        for i in range(len(self.supply)):
            namesRow.append("S"+str(i+1))
        #if dummy == True:
        #    namesRow.append("DUMMY")
        namesRow.append("DEMAND")

        for i in range(len(self.demand)):
            namesColumn.append("D"+str(i+1))
        if self.dummy == True:
            namesColumn[-1] = "DUMMY"
            #self.columns.set(str(int(self.columns.get())+1))
        namesColumn.append("SUPPLY")

        for a in range(len(self.deliveryCosts)):
            temp1 = []
            for b in range(len(self.deliveryCosts[a])):
                temp1.append(self.deliveryCosts[a][b])
            namesValues.append(temp1)
                

        #namesValues = self.deliveryCosts
        #print("apple and pie tatstes nice!",self.deliveryCosts)
        for a in range(len(self.deliveryCosts)):
            namesValues[a].append(self.supply[a])

        for grid in range(2):
            if grid == 1:
                for a in range(len(namesValues)):
                    for b in range(len(namesValues[a])-1):
                        namesValues[a][b] = ""
                if case == 1: # if the case is for the result window, then the information displayed will be the optimal stock solution as well as the original problem.
                    for a in range(len(namesValues)):
                        for b in range(len(namesValues[a])-1):
                            if self.Gcells[self.Gcells[-1].column*a+b].empty == False:
                                namesValues[a][b] = self.Gcells[self.Gcells[-1].column*a+b].stock
                            print("cell passed: "+str(self.Gcells[-1].column*a+b))

            #print(namesValues)
            #print("YETTA FRUIT",self.deliveryCosts)
            #print(namesValues)
                        
            gridFrames.append(tk.Frame(self.Show))
            print("supply: ",self.supply,(2*len(self.supply)+3))
            print("demand: ",self.demand,(2*len(self.demand)+3))

            self.Sframes = []
            print((2*len(self.supply)+3)*(2*len(self.demand)+3))
            for a in range((2*len(self.supply)+3)*(2*len(self.demand)+3)): # the self.Sframes were created so that the correct widgets can be used.
                self.Sframes.append("")

            self.Sframes[0] = tk.Label(gridFrames[grid],text = "X")
            #print(namesColumn)
            nameCounter = 0
            for i in range(1,2*len(self.demand)+3 - 1,2):# this starts at the second array data and pplaces the text and lines for the demand line.
                self.Sframes[i] = tk.Label(gridFrames[grid],text = "|")
                self.Sframes[i+1] = tk.Label(gridFrames[grid],text = namesColumn[nameCounter])
                nameCounter += 1
                #print(nameCounter)
                
            for a in range(1,2*len(self.supply)+3,2): # this jumps every other group of frames to create lines, which organizes what the data is connect to.
                for b in range(2*len(self.demand)+3):
                    #print(a*(2*len(self.demand)+3)+b)
                    #self.Sframes[a*(2*len(self.demand)+3)+b] = tk.Label(self.Sframes[a*(2*len(self.demand)+3)+b],text = "-")###
                    #frames[a*(int(self.columns.get())*2+3)+b+1] = tk.Label(frames[a*(int(self.columns.get())*2+3)+b+1],text = "+")###
                    if b % 2 == 0:
                        self.Sframes[a*(2*len(self.demand)+3)+b] = tk.Canvas(gridFrames[grid],width = 60, height = 10)
                        self.Sframes[a*(2*len(self.demand)+3)+b].create_line(0,5,60,5)
                    else:
                        self.Sframes[a*(2*len(self.demand)+3)+b] = tk.Canvas(gridFrames[grid],width = 5, height = 10)
                        self.Sframes[a*(2*len(self.demand)+3)+b].create_line(0,5,5,5)

            nameCounter = 0
            #print(namesRow)
            for a in range(2,2*len(self.supply)+3 - 1,2): # this uses the frames between the horizontal lines and starts the placements of widgets.
                self.Sframes[a*(2*len(self.demand)+3)] = tk.Label(gridFrames[grid],text = namesRow[nameCounter])### # this just places the name of the row.

                nameCounter2 = 0
                #print(nameCounter)
                for b in range(1,2*len(self.demand)+3 - 1,2): # this creates the widgets that places the entry boxes and the vertical lines.
                    self.Sframes[a*(2*len(self.demand)+3)+b] = tk.Label(gridFrames[grid],text = "|")
                    #print(nameCounter)
                    #print(nameCounter2)
                    #print(" ")
                    self.Sframes[a*(2*len(self.demand)+3)+b+1] = tk.Label(gridFrames[grid],text = str(namesValues[nameCounter][nameCounter2]), width = 10 )
                    if grid == 1:###---###
                        self.changingVSolution.append(a*(2*len(self.demand)+3)+b+1)
                    #EntryPlace.append(a*(int(self.columns.get())*2+3)+b+1) # the entry boxes are placed in the frames array, but their position is placed in the EntryPlace array
                    nameCounter2 += 1
                
                nameCounter += 1
                if grid ==1:
                    self.changingVSolution.pop(-1)
    ########
            nameCounter = 0
            #print(2*len(self.demand)+3)
            ##print(2*len(self.supply)+3)
            #print(len(self.Sframes))
            last = (2*len(self.demand)+3)*((2*len(self.supply)+3) - 1)# is the last line of the grid that is controlled differently than the others, just like the first line of the grid.
            self.Sframes[last] = tk.Label(gridFrames[grid], text = namesRow[-1]) # the last line is like this: NLELELELE...B (N = text,L = line,E = entry box,B = blank), so there is custom code that creates this seperatly.
            for b in range(1,2*len(self.demand)+3 - 2,2):
                #print(last+b)
                #print(last+b+1)
                #print("      ")
                self.Sframes[last+b] = tk.Label(gridFrames[grid],text = "|")
                self.Sframes[last+b+1] = tk.Label(gridFrames[grid],text = str(self.demand[nameCounter]), width = 10)
                #EntryPlace.append(last+b+1)
                nameCounter += 1
                            
            self.Sframes[-2] = tk.Label(gridFrames[grid], text = "|")
            self.Sframes[-1] = tk.Label(gridFrames[grid], text = "X")

            counter = 0
            text = tk.Label(self.Show, text = gridNames[grid])
            seperator = tk.Label(self.Show, text = "")            
            if self.Oresult[0].get() == 0: # this changes the grid depending if the user wanted it.
                
                text.grid(row = 2*grid,column = 0)
                gridFrames[grid].grid(row = 2*grid + 1, column = 0) # grid questions 1, grid solutions 2
                seperator.grid(row = 2*grid+2,column = 0)
                
            else:
                if grid == 0:
                    text.grid(row = 0,column = 0)
                    gridFrames[grid].grid(row = 1,column = 0)
                elif grid == 1:
                    text.grid(row = 0,column = 1)
                    gridFrames[grid].grid(row = 1, column = 1)

            for a in range(2*len(self.supply)+3): # this places the widgets on to the window, making it visible
                for b in range(2*len(self.demand)+3):
                    print(counter)
                    self.Sframes[counter].grid(row = a, column = b)
                    counter += 1

        if case == 0: # if the case was to show the methods, then the SCGrid will be shown. the result window does not need this to display the optimal solution.
            self.SCGrid(gridFrames) # grid shadow costs 3

            self.Controller() # this sets up the mathematical methods.
            for i in range(3):   
                gridFrames.append(tk.Frame(self.Show)) # this applies two extra frames for the extra details and the buttons.
            #gridFrames.append(tk.Frame(self.Show))

            if self.Oresult[0].get() == 0:
                
                gridFrames[-2].grid(row = 1, column = 1) # grid details 4
            else:
                gridFrames[-2].grid(row = 2,column = 1)
            stageName = tk.StringVar()
            stageName.set("stage: "+stageName.get())
            stage = tk.Label(gridFrames[-2],textvariable = stageName)
            stage.grid(row = 0, column = 0)
            
            currentCost = tk.StringVar()
            currentCost.set("current cost: 0") # this will display the cost of the current solution.
            costL = tk.Label(gridFrames[-2],textvariable = currentCost)
            costL.grid(row = 1, column = 0)
            #costList = tk.Text(gridFrames[-2])
            #costList.grid(row = 1, column = 0)
            #costList.insert(tk.INSERT,"cost history:")
            if self.Oresult[0].get() == 0:
                
                gridFrames[-1].grid(row = 3,column = 1) # grid buttons 5 # grid details 4
            else:
                gridFrames[-1].grid(row = 3,column = 1)
            
            button = tk.Button(gridFrames[-1],text = "next step",command = lambda: self.MathSolver(0,stageName,currentCost))# this button allows the user to go to the next step.
            button.grid(row = 0,column = 0)
            button = tk.Button(gridFrames[-1],text = "complete stage",command = lambda: self.MathSolver(1,stageName,currentCost))# this button allows the user to skip a whole stage.
            button.grid(row = 1,column = 0)

        else:
            self.displayInfo(gridFrames)
        
            self.Show.mainloop()

    def displayInfo(self,gridFrames): # this will display which stock goes to which warehouse.
        gridFrames.append(tk.Frame(self.Show))

        columnName = []
        rowName = []
        for i in range(self.Gcells[-1].column):
            columnName.append("D"+str(i+1))

        for i in range(self.Gcells[-1].row):
            rowName.append("S"+str(i+1))
            
        for i in range(len(self.Gcells)):
            if self.Gcells[i].empty == False and self.Gcells[i].stock != 0:
                move = "{} unit(s) from {} to {}".format(self.Gcells[i].stock,rowName[self.Gcells[i].row - 1],columnName[self.Gcells[i].column-1]) # all cells that do have stock will be listed.
                label = tk.Label(gridFrames[-1],text = move)
                label.pack()

        cost = self.costs()
        label = tk.Label(gridFrames[-1],text = "at a cost of £"+str(cost)) # the total cost is calculated for the last time.
        label.pack()
        button = tk.Button(gridFrames[-1],text = "back to start",command = lambda: self.quits(9))# this will bring the user back to the main menu to type in another question
        button.pack(side = "bottom")
        
        gridFrames[-1].grid(row = 5,column = 0)
    def SCGrid(self,gridFrames): # this creates the last grid that contains the shadow costs and improvement indices. this is due to the fact that this grid is begger and have a different structure than the other grids.

        gridFrames.append(tk.Frame(self.Show))
        #counter = 0

        self.CFrames = []
        #size = (2*len(supply)+5)*(2*len(demand)+5)
        #print(size)
        self.changingVSG = [] # this vaiable is used to hold the locations of cells that must change.
        nameBox = []

        for i in range(len(self.demand)):
            nameBox.append("D"+str(i+1))
            
        if self.dummy == True:
            nameBox[-1] = "DUMMY" 
        nameBox.append("SUPPLY")

        for i in range(len(self.supply)):
            nameBox.append("S"+str(i+1))
            nameBox.append(self.supply[i])
        nameBox.append("DEMAND")

        for i in range(len(self.demand)):
            nameBox.append(self.demand[i])
            print(self.demand[i])
        iNames = iter(nameBox)

#        for i in range(size):
#            self.CFrames.append(tk.Frame(gridself.CFrames[2]))

        for a in range((2*len(self.supply)+5)*(2*len(self.demand)+5)):
                self.CFrames.append("")

        ##group 1## row: 1

        for i in range(1,2*len(self.demand)+5,2):
            self.CFrames[i] = tk.Label(gridFrames[-1],text = "|")
        self.CFrames[0] = tk.Label(gridFrames[-1],text = "Shadow Costs")
        self.CFrames[2] = tk.Label(gridFrames[-1],text = "X")
        self.CFrames[2*len(self.demand)+5 - 1] = tk.Label(gridFrames[-1],text = "X")
        for i in range(4,2*len(self.demand)+5-1,2):
            self.CFrames[i] = tk.Label(gridFrames[-1],width = 10)
            self.changingVSG.append(i)

        ##group 2## row: 2,4,6,8,10
    
        for a in range(1,2*len(self.supply)+5,2):
            for b in range(2*len(self.demand)+5):
                if b % 2 == 0:
                    self.CFrames[a*(2*len(self.demand)+5)+b] = tk.Canvas(gridFrames[-1],width = 60, height = 10)
                    self.CFrames[a*(2*len(self.demand)+5)+b].create_line(0,5,60,5)
                else:
                    self.CFrames[a*(2*len(self.demand)+5)+b] = tk.Canvas(gridFrames[-1],width = 5, height = 10)
                    self.CFrames[a*(2*len(self.demand)+5)+b].create_line(0,5,5,5)

        ##group 3## row: 3
        for i in range(1,2*len(self.demand)+5,2):
            self.CFrames[2*(2*len(self.demand)+5)+i] = tk.Label(gridFrames[-1],text = "|")
        self.CFrames[2*(2*len(self.demand)+5)] = tk.Label(gridFrames[-1],text = "X")
        self.CFrames[2*(2*len(self.demand)+5)+2] = tk.Label(gridFrames[-1],text = "X")
        for i in range(4,2*len(self.demand)+5,2):
            self.CFrames[2*(2*len(self.demand)+5)+i] = tk.Label(gridFrames[-1],text = iNames.__next__(),width = 10)


        ##group 4## row: 5,7,9
        for a in range(4,2*len(self.supply)+3,2):
            for b in range(0,2*len(self.demand)+4,2):
                self.CFrames[a*(2*len(self.demand)+5)+b+1] = tk.Label(gridFrames[-1],text = "|")
        #for a in range(4,2*len(self.supply)+5-2,2):

                if b == 2:
                    self.CFrames[a*(2*len(self.demand)+5)+b] = tk.Label(gridFrames[-1],text = iNames.__next__(),width = 10)
                    #constantValues.append(a*(2*len(self.demand)+5)+b)
                else:
                    self.CFrames[a*(2*len(self.demand)+5)+b] = tk.Label(gridFrames[-1],width = 10)
                    self.changingVSG.append(a*(2*len(self.demand)+5)+b)
            self.CFrames[a*(2*len(self.demand)+5)+b+2] = tk.Label(gridFrames[-1],text = iNames.__next__(), width = 10)

        ##group5## row: 11
        for i in range(1,2*len(self.demand)+5,2):
            self.CFrames[(2*len(self.demand)+5)*(2*len(self.supply)+4)+i] = tk.Label(gridFrames[-1],text = "|")
        self.CFrames[(2*len(self.demand)+5)*(2*len(self.supply)+4)] = tk.Label(gridFrames[-1],text = "X")

        for i in range(2,2*len(self.demand)+3,2):
            self.CFrames[(2*len(self.demand)+5)*(2*len(self.supply)+4)+i] = tk.Label(gridFrames[-1],text = iNames.__next__(),width = 10)
            #constantValues.append((2*len(self.demand)+5)*(2*len(self.supply)+5-1)+i)

        self.CFrames[-1] = tk.Label(gridFrames[-1],text = "X")

        counter = 0
        for a in range(2*len(self.supply)+5):
            for b in range(2*len(self.demand)+5):
                self.CFrames[counter].grid(row = a, column = b)
                counter += 1

        Ctext = tk.Label(self.Show,text = "change")
        
        if self.Oresult[0].get() == 0:
            
            Ctext.grid(row = 4,column = 0)
            gridFrames[2].grid(row = 5 ,column = 0)
        else:
            Ctext.grid(row = 2,column = 0)
            gridFrames[2].grid(row = 3,column = 0)

        return gridFrames

    def Controller(self): # this sets up all of the mathematical information for the methods and creates the cells as well.
        self.currentCost = 0
        self.Gcells = []
        self.tempCounter = 0
        self.lastCell = 0
        self.optimal = False
        self.StageCounter = 0

        if sum(self.supply) != sum(self.demand): # this will check if the total sum of supply is equal to the total sum of demand.
            if sum(self.supply) > sum(self.demand):
                for i in range(len(deliveryCosts)): # this just inputs the dummy column that is needed. as the deliveryCosts is organised by
                    deliveryCosts[i].append(0)      # rows, this makes it easier to place cells of 0 cost
                self.demand.append(sum(self.supply)- sum(self.demand)) # this makes sure that the total supply equals the total demand.
        #counter = 0
        for a in range(len(self.deliveryCosts)): # this uses the data from the test data and implements it into objects, then placed in an array.
            for b in range(len(self.deliveryCosts[a])):
                self.Gcells.append(CellCreation(a+1,b+1,self.deliveryCosts[a][b])) # this creates the cells that will be used with the methods.
                #counter += 1

    def MathSolver(self,case,stageName,currentCost): # this will be the operator for the methods used. how the methods are done is that the methods are performed first and we can use the saved information.
        if self.StageCounter == 0: # at the start the north west corner method is performed. 
            stageName.set("stage: north-west corner method") # this will change an label in a window to describe what the user is doing right now.
            self.NorthWestCornerMethod()
            
            if case == 1: # this is used if the user wants to skip pass a stage at first.
                while self.StageCounter != 2: 
                    self.ChangeSolution() # this calls to change the visuals of the grid. this allows the user to know what is happening.
                #self.StageCounter = 2
            else:
                self.StageCounter = 1
                self.ChangeSolution()
            #print(self.StageCounter)
                
        elif self.StageCounter == 1:
            if case == 1: # this will only do the changes so that the methods does not need to be performed again. the if statement allos the user to skip the rest of the method.
                while self.StageCounter != 2: 
                    self.ChangeSolution()
            else:
                self.ChangeSolution()
            #print("its done!")
        elif self.StageCounter == 2: # once the program is done with hte visuals forthe previous method, it moves on to the next.
            currentCost.set("current cost: "+str(self.costs())) # this will calculate the cost of the current solution.
            stageName.set("stage: shadow costs")
            self.tempCounter = 0
            self.rowSC = []
            self.columnSC = []
            self.shadowCosts() # the shadow costs are then calculated.
            if case == 1:
                while self.StageCounter != 4: 
                    self.changeSG()
                #self.StageCounter = 2
            else:
                self.StageCounter = 3
                self.changeSG()
                
            #self.StageCounter = 3
            #self.changeSG()
        elif self.StageCounter == 3:
            if case == 1:
                while self.StageCounter != 4: 
                    self.changeSG()
            else:
                self.changeSG()
            #print("its done!")

        elif self.StageCounter == 4:
            stageName.set("stage: improvement indices")
            self.tempCounter = 0
            self.optimal = self.ImprovementIndices()# this will find the improvement indices.
            if case == 1:
                while self.StageCounter != 6: 
                    self.changeII()
                #self.StageCounter = 2
            else:
                self.StageCounter = 5
                self.changeII()
            #print(self.StageCounter)

        elif self.StageCounter == 5:
            if case == 1:
                while self.StageCounter != 6: 
                    self.changeII()
            else:
                self.changeII()

        elif self.optimal == True and self.StageCounter == 6:
            tk.messagebox.showinfo("completed","this solution is optimal!") # this is used to to show the user that the optimal solution has been found, and to stop the program doing anything else.
            self.quits(5) # this will quit the "show" window and the result window will appear
            self.ShowWindow(1)# instead of a seperate function, an if statement is used to alter which is happening.
                
        elif self.StageCounter == 6 and self.optimal == False:
            stageName.set("stage: stepping-stone method")
            self.tempCounter = 0
            self.SteppingStoneMethod()# the stepping stone method is only done if the current solution is not optimal.
            if case == 1:
                while self.StageCounter != 8: 
                    self.changeSS()
                #self.StageCounter = 2
            else:

                self.StageCounter = 7
                self.changeSS()

        elif self.StageCounter == 7:
            if case == 1:
                while self.StageCounter != 8: 
                    self.changeSS()
            else:
                self.changeSS()
        elif self.StageCounter == 8: # at the end of the cycle, some values of the cells are resetted, and changes the stagecounter to 2, allowing the program to do the shadow costs.
            self.tempCounter = 0
            print("cycle done!")
            self.StageCounter = 2
            #costList.insert(INSERT,str(self.costs()))
            currentCost.set("current cost: "+str(self.costs()))
            for i in range(len(self.changingVSG)):
                self.CFrames[self.changingVSG[i]].config(text = "", bg = "SystemMenu", font = "Calibri 10")

    def NorthWestCornerMethod(self):
        dema = self.demand[:]
        supp = self.supply[:]
        SFD = 0

        for i in range(len(self.Gcells)): # this is the actual north-west corner method.
            if supp[self.Gcells[i].row-1] == 0 or dema[self.Gcells[i].column-1] == 0: # this checks weather the suppy or demand for that row/column is zero.
                pass # if it is zero, then nothing else can be done with the cells in that row/column, so they are ignored.
            else:
                supplyHold = supp[self.Gcells[i].row - 1] # this grabs the cell's row supply
                demandHold = dema[self.Gcells[i].column - 1]# this grabs the cell's column demand

                leftOverStock = supplyHold - demandHold # this calculates how much stock is left over after the transaction from the supply to demand
                if leftOverStock < 0: # if the stock left over is negative, this is just saying that the demand needs more stock but there is no more.
                    leftOverStock = 0 # therefore, the left over stock is equal to zero

                leftOverDemand = demandHold - supplyHold # this is the same as the left over stock but, calculate the left over demand amount.
                if leftOverDemand < 0:
                    leftOverDemand = 0 # again, if the demand amount left over is negative, this is just saying that there is more stock but the demand is full.

                if leftOverDemand == 0: # this calculates how much stock the cell actually has after the transaction.
                    self.Gcells[i].stock = demandHold # if the left over demand is zero, then the cell is equal to the size of the demand, as it's full.
                    self.Gcells[i].empty = False
                    SFD += 1
                else:
                    self.Gcells[i].stock = (dema[self.Gcells[i].column - 1]) - leftOverDemand # if not, then it is the demand column take away the left over demand.
                    self.Gcells[i].empty = False
                    SFD += 1
                    
                supp[self.Gcells[i].row - 1] = leftOverStock # the left over stock and demand are then updated the supply and demand information to be carried over to the next cell
                dema[self.Gcells[i].column - 1] = leftOverDemand

         # this part of the code detects whether the current solution is degenerate(no.rows+no.columns - 1)
        if SFD < (self.Gcells[-1].row + self.Gcells[-1].column - 1): # if this is true, then that means the problem is a degenerate problem and a zero need to be placed somewhere.
            for a in range(self.Gcells[-1].row-1):
                
                for b in range(self.Gcells[-1].column-1): # this searches in the area where an "out of bounds" error is not produced.
                    area = [False,False] # this is used to determine if the cell has another cell connected to it
                    if self.Gcells[self.Gcells[-1].column*a+b].empty == False:
                        #print("non-empty cell found!")
                        #print("cell {}".format(self.Gcells[-1].row*a+b))
                        
                        if self.Gcells[self.Gcells[-1].column*a+b+1].stock == 0:
                            area[0] = True # this checks the cell next to the current cell.

                        if self.Gcells[self.Gcells[-1].column*(a+1)+b].stock == 0:
                            area[1] = True # this checks the cell below the current cell.
                        #print(area)
                        if False not in area: # this checks if the cell has empty spaces in both cells connected to it.
                            print("nothing exists!")
                            self.Gcells[self.Gcells[-1].column*a+b+1].empty = False # this creates an degenerate cell.
                            print("column found! cell: ",(self.Gcells[-1].column*a+b+1))
                            #break
                    
    def shadowCosts(self):
        ####
        #rowSC = []
        #columnSC = []
        rowDone = []
        columnDone = []
        self.orderSC = ["R0"] # this provides very useful things. first off, this will list the cells that are discovered first by "R" for row or "C" column and then the number of the cell its on.
        
        for i in range(self.Gcells[-1].row): # this is used to check if the row and column has been checked and have got an result.
            self.rowSC.append(0)
            rowDone.append(False)
        for i in range(self.Gcells[-1].column):
            self.columnSC.append(0)
            columnDone.append(False)

        rowDone[0] = True
        while(1): # in some cases, a row/column is discovered after the last row/column, so this while loop is needed so the program can check the previous rows/columns
            for a in range(self.Gcells[-1].row): # this checks each row
                while(2): # this checks each columns to make sure that all of the values have been calculated and recieved in the arrays.
                    done = True # this is to check the row and column numbers so nothing is missed
                    for b in range(self.Gcells[-1].column): # this checks each column, therefore checking each cell.
                        #print("apple",a,b)
                        noRow = False
                        noColumn = False
                        # this piece of code checks if there is a row shadow cost missing or vice versa for the column
                        if rowDone[a] == False and self.Gcells[self.Gcells[-1].column*a+b].empty == False: #rowSC[a] == 0 and
                            noRow = True
                        if columnDone[b] == False and self.Gcells[self.Gcells[-1].column*a+b].empty == False: # columnSC[b] == 0 and
                            noColumn = True

                        if noRow == noColumn: # if both row and column are the same, then there is only two possible reasons, both shadow costs have value, or no value can be used to find the other value
                            print("same-same")
                            pass
                        elif noRow == False and noColumn == True: # is there is a row shadow cost number but no column, then the column shadow cost is calculated.
                            print("false-true")
                            done = False
                            #for i in range(len(self.Gcells)):
                            #    self.Gcells[i].info()
                            #print(self.Gcells[self.Gcells[-1].column*a+b].cost)
                            columnShadowCost = self.Gcells[self.Gcells[-1].column*a+b].cost - self.rowSC[a] # this will calculate the column shadow cost.
                            self.columnSC[b] = columnShadowCost
                            columnDone[b] = True # this tells us by saying that the shadow cost has been found for that column
                            self.orderSC.append("C"+str(b))

                        elif noRow == True and noColumn == False: # this is vice versa for the row.
                            print("true-false")
                            done = False
                            rowShadowCost = self.Gcells[self.Gcells[-1].column*a+b].cost - self.columnSC[b] # this will calculate the column shadow cost.
                            self.rowSC[a] = rowShadowCost
                            rowDone[a] = True
                            self.orderSC.append("R"+str(a))

                        print(self.rowSC)
                        print(rowDone)
                        print(self.columnSC)
                        print(columnDone)
                    if done == True: # if all of the columns have either got values on both sides or no values, this means that there is nothing else that can be done.
                        break
            if False not in rowDone and False not in columnDone: # if all of the columns and rows have a shadow cost, then the method has been done
                break

    def ImprovementIndices(self): # this is to find the improvement indices
        ECData = [0,0]# [position in Gcell, lowest value]
        for i in range(len(self.Gcells)):
            if self.Gcells[i].stock == 0: # the improvement indices can only be calculated using cells with no stock, so this filters the array.
                self.Gcells[i].II = self.Gcells[i].cost - self.rowSC[self.Gcells[i].row -1] - self.columnSC[self.Gcells[i].column -1]# the improvement indices is calculated by the cost take away the the RowSC and the columnSC of the cells row and column
                if self.Gcells[i].II < ECData[1]:
                    ECData[0] = i
                    ECData[1] = self.Gcells[i].II
                self.Gcells[i].info()
        print(ECData)
        if ECData[0] == 0:
            print("it's optimal!")
            return True
        self.Gcells[ECData[0]].EC = True
        self.Gcells[ECData[0]].empty = False # this classify that even if the cells.stock is zero, it goes under the same effect as a cell with stock, so this clafiys this
        self.Gcells[ECData[0]].change = "+"
        return False #this returns the cell ready for the next operation

    def SteppingStoneMethod(self):
        #done = False
        no1 = False

        while(no1 == False): # this checks the cells and decides if the cells can be used to solve the problem of the stepping stone method.
            # for example: if you have 1 cell with stock in a row/column, it will be impossible to find another cell that can hold the opposite change as there is only 1 cell.
            UCellCountRow = [] # "useable cell count row" this will hold infomation on how many cells can be used in the current problem 
            UCellCountColumn = []# same for this. an example of this data is [1,2,1,2]
            
            for i in range(self.Gcells[-1].row):
                UCellCountRow.append(0) # this is used as placement for the values that are being added later.
            for i in range(self.Gcells[-1].column):
                UCellCountColumn.append(0)

            
            for i in range(len(self.Gcells)):
                self.Gcells[i].info()
                if self.Gcells[i].DNU == False and (self.Gcells[i].empty == False or self.Gcells[i].EC == True) : 
                    #print(Gcells[i].row - 1)
                    #print(i)
                    UCellCountRow[self.Gcells[i].row - 1] += 1
                    UCellCountColumn[self.Gcells[i].column - 1] += 1 # this will say to the arrays that a cell is useable on the grid.
                    #print(i)
            #print("useable cells")
            #print(UCellCountRow)
            #print(UCellCountColumn)        

            for a in range(len(UCellCountRow)): # this is a checker for the rows
                if UCellCountRow[a] == 1: # this checked if there is only one useable cell in that row
                    for b in range(len(self.Gcells)): # "for a row/column to have one stock cell,this means that the row/column is the same as the row/column of the cell." so the cells are checked.
                        if self.Gcells[b].row == a + 1 and self.Gcells[b].empty == False: # so the cells are checked if, in this case, have the samy row position and have stock.
                            self.Gcells[b].DNU = True # the cells will be classified as "do not use". this means that they cannot be used in the actuall stepping stone method.
                            #notCells.append(b)

            for a in range(len(UCellCountColumn)): # this is the same as the rows, but fro the columns
                if UCellCountColumn[a] == 1:
                    for b in range(len(self.Gcells)):
                        if self.Gcells[b].column == a + 1 and self.Gcells[b].empty == False:
                            self.Gcells[b].DNU = True
                            #notCells.append(b)

            if 1 in UCellCountColumn or 1 in UCellCountRow: # if a 1 is detected in either array, then it's not suitable for the method.
                pass
            else:
                no1 = True # this tells that loop that there is no 1's in both arrays and therefore another solution can be found

        counter = 0
        while(1):#done == False): ##############
            rowC = [] # these arrays will hold information based on how many cells are affected, and what changes have been already made.
            columnC = []
            rowUse = False


            for i in range(self.Gcells[-1].row): 
                rowC.append([0])
                UCellCountRow.append(0) 
            for i in range(self.Gcells[-1].column):
                columnC.append([0])
                UCellCountColumn.append(0)

            for i in range(len(self.Gcells)): # this adapt the arrays to include information about the cells and the changes if any.
                if self.Gcells[i].change != "":
                    rowC[self.Gcells[i].row-1][0] += 1
                    rowC[self.Gcells[i].row-1].append(self.Gcells[i].change)
                
                    columnC[self.Gcells[i].column - 1][0] +=1
                    columnC[self.Gcells[i].column-1].append(self.Gcells[i].change)

            #print("after: ",rowC)
            #print("after: ",columnC)

            rowLocation = "D" # this is used as an indicator to see if the right amount of changes have been done.
            for i in range(len(rowC)):
                if 1 in rowC[i]: # this loop will find the first array in the rowC array that does not satify the rules of the method([1,+] is not correct so the location is recorded and will tell to make changes to the row. vice visa for the column
                    rowLocation = i
                    rowUse = True
                    break

            columnLocation = "D" # this is used as an indicator to see if the right amount of changes have been done.
            for i in range(len(columnC)):
                if 1 in columnC[i]:
                    columnLocation = i
                    break

            if rowLocation == "D" and columnLocation == "D": # if there are no "1" in the arrays of columnC and rowC, then that means that the process is complete and follows the rules.
                break

            if rowUse == True: # to keep organized during the process of the stepping stone method, the row is checked first. after that, if the row is fine then the column is checked.

                for i in range(len(self.Gcells)):
                    if self.Gcells[i].row == rowLocation + 1 and  self.Gcells[i].change == "" and self.Gcells[i].DNU != True and (self.Gcells[i].empty == False) :# find the first cell that is: in the same row as the row we're looking at, has no change, not classified as "do not use", and can either have stock or be degenerate.
                        #Gcells[i].SSinfo()
                        Acell = i
                        break # to only record the first cell, break has to be used to end the loop.

                if "+" in rowC[rowLocation]: # "there can only be one increasing and decreasing cell in each row/column" this gives the cell the opposite change of the change that is already recorded.
                    self.Gcells[Acell].change = "-"
                else:
                    self.Gcells[Acell].change = "+"

                self.Gcells[i].SSinfo() # used to call information

            else: # this acts the same as the row, but this time, the column is checked
                
                for i in range(len(self.Gcells)):
                    if self.Gcells[i].column == columnLocation + 1 and self.Gcells[i].change == "" and self.Gcells[i].DNU != True and (self.Gcells[i].empty == False):
                        #Gcells[i].SSinfo()
                        Acell = i
                        break

                if "+" in columnC[columnLocation]:
                    self.Gcells[Acell].change = "-"
                else:
                    self.Gcells[Acell].change = "+"

        #### this part of the code is to find the smallest negative number which has a change.
        cellCAE = [] # cell change and effect. this is a two dimensional array that records the cell,s [change,stock] as the stock is affected by the change.
        for i in range(len(self.Gcells)):
            if self.Gcells[i].change != "":
                cellCAE.append([self.Gcells[i].change,self.Gcells[i].stock])
        print(cellCAE)
        #cellCAE = [["-",10],["+",2],["-",3],["+",10],["-",2],["-",5]] # this is to test the data

        lowest = 0 # this will hold the smallest negative number of the changes
        first = False # as all of the numbers are bigger than 0 if it is just a number, then the first cell with a negative will be compared with the other negative cells.

        for i in range(len(cellCAE)):
            if cellCAE[i][0] == "-" and first == False:
                lowest = cellCAE[i][1] # the first negative cell value will be recorded for comparison.
                first = True
            elif cellCAE[i][0] == "-":
                if cellCAE[i][1] < lowest:
                    lowest = cellCAE[i][1] # if the cell stock value is smaller than the value of lowest, then the cell value will replace the lowest value.

        print(lowest)

        cellDone = False
        for i in range(len(self.Gcells)):
            cellDone = self.Gcells[i].ApplyChange(lowest,cellDone) # this calls the object function that apply the changes depending on the change and the lowest value.

    def costs(self):
        Tcost = 0 # this holds the total cost of the solution
        for i in range(len(self.Gcells)):
            Tcost += (self.Gcells[i].cost * self.Gcells[i].stock) # this adds the individual costs of each cell to the total cost
        #self.currentCost = Tcost
        return Tcost
        #self.costHistory.append(Tcost)

    def ChangeSolution(self): # this will change the visual cells if they have stock.
        if self.tempCounter < len(self.Gcells):

            if self.Gcells[self.tempCounter].empty != True :

                location = (self.Gcells[-1].column*(self.Gcells[self.tempCounter].row - 1)+(self.Gcells[self.tempCounter].column - 1))# this, using the row and column number, will find the location of that cell within an array.
                print(self.tempCounter)
                self.Sframes[self.changingVSolution[location]].config(text = str(self.Gcells[self.tempCounter].stock)) # this will change the visuals that the user will see.
                self.tempCounter += 1

                for i in range(self.tempCounter,len(self.Gcells)):
                    
                    if self.Gcells[i].empty == True: # to save the user from clicking all of the time, this code will find the next non-empty cell so that the user justs need to click once.
                        self.tempCounter += 1
                    else:
                        break
        if self.tempCounter == len(self.Gcells): # this will end this function and prevent it from doing it again unless its the next cycle, by changing what stage is next.
            #if self.tempCounter == len(self.Gcells):
            #self.Sframes[self.changingVSolution[-1]].config(text = str(self.Gcells[self.tempCounter - 1].stock))
            #print("its complete!")
            #pass # this might contain the function to change the method label
            self.StageCounter = 2

    def changeSG(self): # this will change the visuals for the shadow costs.
        if self.tempCounter < len(self.orderSC):
            row = []
            column = []
            cells = []
            #print(self.changingVSG)
            for a in range(len(self.demand)): # as the array containing the locations of the changing cells has both the shadow cost cells and improvement indices cells, they need to be seperated.
                column.append(self.changingVSG[a])
            for b in range(a+1,len(self.changingVSG),len(self.demand)+1):
                row.append(self.changingVSG[b])
                for i in range(1,len(self.demand)+1):
                    cells.append(self.changingVSG[b+i])
            #print(cells)
            if self.tempCounter == 0 :
                for i in range(len(self.Gcells)):
                    #print(i)
                    #print(cells[i])
                    #print(self.Gcells[i].cost)
                    if self.Gcells[i].empty != True:
                        self.CFrames[cells[i]].config(text = self.Gcells[i].cost,font = "Calibri 10 bold")

            temp1 = self.orderSC[self.tempCounter] # using the tempCounter, an string is used within the array.
            #print(temp1[0])
            if temp1[0] == "R": # the string is seperated into two types: the location and the location number.
                #print("row")
                #print(self.rowSC[int(temp1[1:])])
                #print("at ",row[int(temp1[1:])])
                self.CFrames[row[int(temp1[1:])]].config(text = self.rowSC[int(temp1[1:])],font = "Calibri 10", fg = "firebrick2") 
            else:
                #print("column")
                #print(self.columnSC[int(temp1[1:])])
                #print("at ",column[int(temp1[1:])])
                self.CFrames[column[int(temp1[1:])]].config(text = self.columnSC[int(temp1[1:])],font = "Calibri 10", fg = "firebrick2")
            self.tempCounter += 1
        if self.tempCounter == len(self.orderSC):# this ends the functions and moves to the next stage.
            #print("all done!")
            self.StageCounter = 4

    def changeII(self): # this will change the visuals for the improvement indices.
        ##########
        cells = []
        #print(self.changingVSG)
        #for a in range(len(self.demand)):
        #column.append(self.changingVSG[a])
        for b in range(len(self.demand),len(self.changingVSG),len(self.demand)+1):
            for i in range(1,len(self.demand)+1):
                cells.append(self.changingVSG[b+i]) # this will find the locations of changeable cells but not the "shadow cost" cells.

        if self.tempCounter < len(self.Gcells): # this works similarly to the changes for the soloution
            if self.Gcells[self.tempCounter].empty == True or self.Gcells[self.tempCounter].EC == True:

                self.CFrames[cells[self.tempCounter]].config(text = self.Gcells[self.tempCounter].II, font = "Calibri 10 bold",bg = "gold2")# the colour change will allow students at the back of the classrooms to see the difference.
                self.tempCounter += 1
                
            for i in range(self.tempCounter,len(self.Gcells)):
                if self.Gcells[i].empty == False and self.Gcells[i].EC == False:
                    self.tempCounter += 1
                else:
                    break
        elif self.tempCounter == len(self.Gcells):
            print("COMPLETED")
            for i in range(len(self.Gcells)):
                if self.Gcells[i].EC == True: # once the user has finished with the visuals the entering cell is located.
                    self.CFrames[cells[i]].config(bg = "DarkOrange1")# the entering cell will change colour to show that its the entering cell.
                    location = (self.Gcells[-1].column*(self.Gcells[i].row - 1)+ self.Gcells[i].column - 1)
                    self.Sframes[self.changingVSolution[location]].config(text = chr(952))# this will add a theta on the "solution" grid in the same number location as the entering cell in the "change" grid
                    break # there's no need to continue if there is only one cell labeled as the entering cell at all times.
            #for i in range(len(self.Gcells)):
            #    self.Gcells[i].SSinfo()
            self.StageCounter = 6

    def changeSS(self): # this will change the visuals of the changes being made to the stock.
        if self.tempCounter < len(self.Gcells):
            #print(self.tempCounter)
            if self.Gcells[self.tempCounter].change != "":
                location = (self.Gcells[-1].column*(self.Gcells[self.tempCounter].row - 1)+ self.Gcells[self.tempCounter].column - 1)
                self.Sframes[self.changingVSolution[location]].config(text = str(self.Gcells[self.tempCounter].stock) + self.Gcells[self.tempCounter].change + chr(952))
                self.tempCounter += 1
                
            for i in range(self.tempCounter,len(self.Gcells)): # again, cells are skipped if they do not need to be changed.
                if self.Gcells[i].change == "" or self.Gcells[i].EC == True:
                    print("pass: ",i)
                    self.tempCounter += 1
                else:
                    break
        else: # once the visuals are done, the cells are resetted, and the changes shown on the grid are done showing a completly different solution.
            #print("ITS COMPLETE") 
            #self.tempCounter = 0
            for i in range(len(self.Gcells)):
                self.Gcells[i].resetCell()
                if self.Gcells[i].empty == False:
                    location = (self.Gcells[-1].column*(self.Gcells[i].row - 1)+ self.Gcells[i].column - 1)
                    self.Sframes[self.changingVSolution[location]].config(text = str(self.Gcells[i].stock))
                else:
                    location = (self.Gcells[-1].column*(self.Gcells[i].row - 1)+ self.Gcells[i].column - 1)
                    self.Sframes[self.changingVSolution[location]].config(text = "")
            self.StageCounter = 8

app = Application() # this starts the whole program

This snippet took 0.09 seconds to highlight.

Back to the Entry List or Home.

Delete this entry (admin only).