#################################################
# hw5.py
# name:
# andrew id:
#################################################

import cs112_f22_week5_linter
import math, copy

from cmu_112_graphics import *


#################################################
# Helper functions
#################################################

def almostEqual(d1, d2, epsilon=10**-7): #helper-fn
    # note: use math.isclose() outside 15-112 with Python version 3.5 or later
    return (abs(d2 - d1) < epsilon)

import decimal
def roundHalfUp(d): #helper-fn
    # Round to nearest with ties going away from zero.
    rounding = decimal.ROUND_HALF_UP
    # See other rounding options here:
    # https://docs.python.org/3/library/decimal.html#rounding-modes
    return int(decimal.Decimal(d).to_integral_value(rounding=rounding))

def rgbString(red, green, blue):
     return f'#{red:02x}{green:02x}{blue:02x}'

#################################################
# Helper functions for lists
#################################################

def print2dList(data):
    # Simple print of 2D list data
    # New line for each row, spaces between columns
    size1, size0 = len(data), len(data[0])
    for i in range(size1):
        for j in range(size0):
            value = data[i][j]
            print(value, end=' ')
        # New line after row
        print()

def print3dList(data):
    # Simple print of 2D list data
    # New line for each row, spaces between columns
    # Extra new line between slices
    size2, size1, size0 = len(data), len(data[0]), len(data[0][0])
    for i in range(size2):
        for j in range(size1):
            for k in range(size0):
                value = data[i][j][k]
                print(value, end=' ')
            # New line after row
            print()
        # Extra new line
        print()

def list1DTo3D(list1D, size0, size1, size2):
    # Create a 3D list with size0 rows, size1 columns, and size2 slices and
    # fill sequentially with values from list1D
    
    n = len(list1D)
    if n != size0*size1*size2:
        dimStr = f"({size0}, {size1}, {size2})"
        print(f"Length of list ({n}) doesn't match 3D dimensions {dimStr}.")
        return None
    
    list3D = []
    index1D = 0
    for i in range(size2):
        slice = []
        for j in range(size1):
            row = []
            for k in range(size0):
                value = list1D[index1D]
                index1D += 1
                row.append(value)
            slice.append(row)
        list3D.append(slice)

    return list3D

def createDecimalImage(size0, size1):
    # Create a 2D list with size0 rows and size1 columns and
    # fill with rowIndex + colIndex*10
    im = []
    for i in range(size1):
        row = []
        for j in range(size0):
            val = j + i*10
            row.append(val)
        im.append(row)

    return im

def createDecimalVolume(size0, size1, size2):
    # Create a 3D list with size0 rows, size1 columns, and size2 slices and
    # fill with rowIndex + colIndex*10 + sliceIndex*100
    vol = []
    for i in range(size2):
        slice = []
        for j in range(size1):
            row = []
            for k in range(size0):
                val = k + j*10 + i*100
                row.append(val)
            slice.append(row)
        vol.append(slice)

    return vol

def flattenImage(imList2D):
    # Converts 2D list image to 1D list
    imList1D = []
    for row in imList2D:
        imList1D += row
    return imList1D

def loadMedicalImageVolume():
    filename = "head_ct_50_50_50.csv"
    rowsPerImage = 50

    vol = []
    with open(filename) as f:
        lineIndex = 0
        for line in f.readlines():
            if lineIndex % rowsPerImage == 0:
                slice = []

            # Split by commas and convert strings to int
            row = []
            for val in line.split(','):
                row.append(int(val))
            slice.append(row)

            if lineIndex % rowsPerImage == 0:
                vol.append(slice)

            lineIndex += 1
            
    return vol

#################################################
# Functions for you to write
#################################################

def nondestructiveRemoveRowAndCol(A, row, col):
    # remember: do not copy or deepcopy A here.
    # instead, directly construct the result
    return 42

def destructiveRemoveRowAndCol(A, row, col):
    return 42

def isKingsTour(board):
    return 42

def isMagicSquare(L):
    return 42

def wordSearchWithIntegerWildcards(board, word):
    return 42

def panImage(im, dx, dy, backgroundValue):
    return 42

def volSlicer(vol, sliceIndex, sliceAxis):
    return 42

def maximumIntensityProjection(vol, maxAxis):
    return 42

#################################################
# 3D Medical Image Viewer
#################################################

def appStarted(app):
    # Load the medical image volume as a 3D list
    # This app.vol should never change
    app.vol = loadMedicalImageVolume()

    # DO NOT CHANGE these app settings
    app.imageDisplayWidth = 400
    app.imageDisplayHeight = 400

    # TODO: Add any other app startup code you need here



    # Keep this here to initialize your first image
    # updateImage will also be called everytime you change app settings that 
    # affect your image
    updateImage(app)

def updateImage(app):
    # Call volSlicer, maximumIntensityProjection, and panImage as necessary to
    # converty the app.vol into a 2D list image named im.
    # Store your resulting image as im (which is then passed to imageToTk
    # at the bottom of this funciton).

    # TODO: YOUR CODE HERE
    # You'll want to change the line below (and add others)
    im = app.vol[len(app.vol)//2]



    # Convert your 2D list im to an image that can be display by Tkinter
    # IMPORTANT: Keep this line at the bottom of updateImage and don't change it
    app.imTk = imageToTk(im, app.imageDisplayWidth, app.imageDisplayHeight)

def imageToTk(im, displayWidth, displayHeight):
    # DO NOT CHANGE this function
    # You don't need to understand this function
    # In short, in converts your 2D image list into an image that can be
    # displayed directly with Tkinter
    imHeight, imWidth = len(im), len(im[0])
    imPIL = Image.new('L', (imWidth, imHeight))
    imPIL.putdata(flattenImage(im))
    imPIL = imPIL.resize((displayWidth, displayHeight), Image.NEAREST)
    return ImageTk.PhotoImage(imPIL)

# Add 112 graphics controller functions here
# IMPORTANT: Be sure to call updateImage anytime you change anything in app
# that affects your image
# We started you with a blank keyPressed function.
# You can add other controller methods here as well if you'd like

def keyPressed(app, event):
    # Change app settings based on key press events
    # TODO: YOUR CODE HERE


    # Make sure to call this anytime you change anything in app that affects
    # your image
    updateImage(app)

def drawInstructions(app, canvas, x0, y0, x1, y1):
    canvas.create_rectangle(x0, y0, x1, y1, fill='lightblue', outline='')
    # Add your code to display instuctions text
    # (feel free to modify/delete the rectangle line above)
    # TODO: YOUR CODE HERE
    canvas.create_text(x0+10, y0+10,
            text="--> put your instructions in this space", anchor='nw')


def redrawAll(app, canvas):
    # DO NOT CHANGE anything in this function

    drawInstructions(app, canvas, 400, 0, 800, 400)
    
    if app.imTk is not None:
        canvas.create_image(0, 0, image=app.imTk, anchor='nw')

def startViewer():
    # DO NOT CHANGE anything in this function
    runApp(width=800, height=400)


#################################################
# Bonus/Optional
#################################################

def makeWordSearch(wordList, replaceEmpties):
    return 42

#################################################
# Test Functions (#ignore_rest)
#################################################

def testNondestructiveRemoveRowAndCol():
    print('Testing nondestructiveRemoveRowAndCol()...')
    a = [ [ 2, 3, 4, 5],[ 8, 7, 6, 5],[ 0, 1, 2, 3]]
    aCopy = copy.copy(a)
    assert(nondestructiveRemoveRowAndCol(a, 1, 2) == [[2, 3, 5], [0, 1, 3]])
    assert(a == aCopy)
    assert(nondestructiveRemoveRowAndCol(a, 0, 0) == [[7, 6, 5], [1, 2, 3]])
    assert(a == aCopy)
    b = [[37, 78, 29, 70, 21, 62, 13, 54, 5],
    [6,     38, 79, 30, 71, 22, 63, 14, 46],
    [47,    7,  39, 80, 31, 72, 23, 55, 15],
    [16,    48, 8,  40, 81, 32, 64, 24, 56],
    [57,    17, 49, 9,  41, 73, 33, 65, 25],
    [26,    58, 18, 50, 1,  42, 74, 34, 66], 
    [67,    27, 59, 10, 51, 2,  43, 75, 35],
    [36,    68, 19, 60, 11, 52, 3,  44, 76],
    [77,    28, 69, 20, 61, 12, 53, 4,  45]]

    c = [[37, 78, 29, 70, 21, 62,     54, 5],
    [6,     38, 79, 30, 71, 22,     14, 46],
    [47,    7,  39, 80, 31, 72,     55, 15],
    [16,    48, 8,  40, 81, 32,     24, 56],
    [57,    17, 49, 9,  41, 73,     65, 25],
    [26,    58, 18, 50, 1,  42,     34, 66], 
    [67,    27, 59, 10, 51, 2,      75, 35],
    [36,    68, 19, 60, 11, 52, 44, 76]]

    bCopy = copy.copy(b)
    assert(nondestructiveRemoveRowAndCol(b,8,6) == c)
    assert(b == bCopy)
    print('Passed!')

def testDestructiveRemoveRowAndCol():
    print("Testing destructiveRemoveRowAndCol()...")
    A = [ [ 2, 3, 4, 5],
          [ 8, 7, 6, 5],
          [ 0, 1, 2, 3]
        ]
    B = [ [ 2, 3, 5],
          [ 0, 1, 3]
        ]
    assert(destructiveRemoveRowAndCol(A, 1, 2) == None)
    assert(A == B) # but now A is changed!
    A = [ [ 1, 2 ], [3, 4] ]
    B = [ [ 4 ] ]
    assert(destructiveRemoveRowAndCol(A, 0, 0) == None)
    assert(A == B)
    A = [ [ 1, 2 ] ]
    B = [ ]
    assert(destructiveRemoveRowAndCol(A, 0, 0) == None)
    assert(A == B)
    print("Passed!")

def testIsKingsTour():
    print("Testing isKingsTour()...")
    a = [ [  3, 2, 1 ],
          [  6, 4, 9 ],
          [  5, 7, 8 ] ]
    assert(isKingsTour(a) == True)
    a = [ [  2, 8, 9 ],
          [  3, 1, 7 ],
          [  4, 5, 6 ] ]
    assert(isKingsTour(a) == True)
    a = [ [  7, 5, 4 ],
          [  6, 8, 3 ],
          [  1, 2, 9 ] ]
    assert(isKingsTour(a) == True)
    a = [ [  7, 5, 4 ],
          [  6, 8, 3 ],
          [  1, 2, 1 ] ]
    assert(isKingsTour(a) == False)
    a = [ [  3, 2, 9 ],
          [  6, 4, 1 ],
          [  5, 7, 8 ] ]
    assert(isKingsTour(a) == False)
    a = [ [  3, 2, 1 ],
          [  6, 4, 0 ],
          [  5, 7, 8 ] ]
    assert(isKingsTour(a) == False)
    a = [ [  1, 2, 3 ],
          [  7, 4, 8 ],
          [  6, 5, 9 ] ]
    assert(isKingsTour(a) == False)
    a = [ [ 3, 2, 1 ],
          [ 6, 4, 0 ],
          [ 5, 7, 8 ] ]
    assert(isKingsTour(a) == False)
    print("Passed!")

def testIsMagicSquare():
    print("Testing isMagicSquare()...")
    assert(isMagicSquare([[42]]) == True)
    assert(isMagicSquare([[2, 7, 6], [9, 5, 1], [4, 3, 8]]) == True)
    assert(isMagicSquare([[4-7, 9-7, 2-7], [3-7, 5-7, 7-7], [8-7, 1-7, 6-7]])
           == True)
    a = [[7  ,12 ,1  ,14],
         [2  ,13 ,8  ,11],
         [16 ,3  ,10 ,5],
         [9  ,6  ,15 ,4]]
    assert(isMagicSquare(a))
    a = [[113**2, 2**2, 94**2],
         [ 82**2,74**2, 97**2],
         [ 46**2,127**2,58**2]]
    assert(isMagicSquare(a) == False)
    a = [[  35**2, 3495**2, 2958**2],
         [3642**2, 2125**2, 1785**2],
         [2775**2, 2058**2, 3005**2]]
    assert(isMagicSquare(a) == False)
    assert(isMagicSquare([[1, 2], [2, 1]]) == False)
    assert(isMagicSquare([[0], [0]]) == False) # Not square!
    assert(isMagicSquare([[1, 1], [1, 1]]) == False) # repeats
    assert(isMagicSquare('do not crash here!') == False)
    assert(isMagicSquare(['do not crash here!']) == False)
    assert(isMagicSquare([['do not crash here!']]) == False)
    print("Passed!")

def testWordSearchWithIntegerWildcards():
    print("Testing wordSearchWithIntegerWildcards()...")
    board = [ [ 'd', 'o', 'g' ],
              [ 't', 'a', 'c' ],
              [ 'o', 'a', 't' ],
              [ 'u', 'r', 'k' ],
            ]
    assert(wordSearchWithIntegerWildcards(board, "dog") == True)
    assert(wordSearchWithIntegerWildcards(board, "cat") == True)
    assert(wordSearchWithIntegerWildcards(board, "tad") == True)
    assert(wordSearchWithIntegerWildcards(board, "cow") == False)
    board = [ [ 'd', 'o',  1  ],
              [  3 , 'a', 'c' ],
              [ 'o', 'q' ,'t' ],
            ]
    assert(wordSearchWithIntegerWildcards(board, "z") == True)
    assert(wordSearchWithIntegerWildcards(board, "zz") == False)
    assert(wordSearchWithIntegerWildcards(board, "zzz") == True)
    assert(wordSearchWithIntegerWildcards(board, "dzzzo") == True)
    assert(wordSearchWithIntegerWildcards(board, "dzzo") == True)
    assert(wordSearchWithIntegerWildcards(board, "zzzd") == True)
    assert(wordSearchWithIntegerWildcards(board, "zzzo") == True)
    board = [ [ 3 ] ]
    assert(wordSearchWithIntegerWildcards(board, "zz") == False)
    assert(wordSearchWithIntegerWildcards(board, "zzz") == True)
    assert(wordSearchWithIntegerWildcards(board, "zzzz") == False)
    board = [ [ 'a', 'b', 'c' ],
              [ 'd',  2 , 'e' ],
              [ 'f', 'g', 'h' ]]
    assert(wordSearchWithIntegerWildcards(board, "aqqh") == True)
    assert(wordSearchWithIntegerWildcards(board, "aqqhh") == False)
    assert(wordSearchWithIntegerWildcards(board, "zz") == True)
    assert(wordSearchWithIntegerWildcards(board, "zzc") == True)
    assert(wordSearchWithIntegerWildcards(board, "zaz") == False)
    print("Passed!")

def testPanImage():
    print("Testing panImage()...")
    height = 3
    width = 4
    imIn = createDecimalImage(width, height)
    imIn_copy = copy.deepcopy(imIn)
    # print2dList(imIn)
    # print()

    dx=0
    dy=0
    expectedResult = [
            [0, 1, 2, 3], 
            [10, 11, 12, 13], 
            [20, 21, 22, 23]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=0
    dy=2
    expectedResult = [
            [99, 99, 99, 99], 
            [99, 99, 99, 99], 
            [0, 1, 2, 3]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=2
    dy=-2
    expectedResult = [
            [99, 99, 20, 21], 
            [99, 99, 99, 99], 
            [99, 99, 99, 99]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=2
    dy=0
    expectedResult = [
            [99, 99, 0, 1], 
            [99, 99, 10, 11], 
            [99, 99, 20, 21]
        ]    
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=-2
    dy=0
    expectedResult = [
            [2, 3, 99, 99], 
            [12, 13, 99, 99], 
            [22, 23, 99, 99]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=-2
    dy=2
    expectedResult = [
            [99, 99, 99, 99], 
            [99, 99, 99, 99], 
            [2, 3, 99, 99]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=-4
    dy=-2
    expectedResult = [
            [99, 99, 99, 99], 
            [99, 99, 99, 99], 
            [99, 99, 99, 99]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=-4
    dy=0
    expectedResult = [
            [99, 99, 99, 99], 
            [99, 99, 99, 99], 
            [99, 99, 99, 99]
        ]
    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    dx=4
    dy=2
    expectedResult = [
            [99, 99, 99, 99], 
            [99, 99, 99, 99], 
            [99, 99, 99, 99]
        ]

    assert(panImage(imIn, dx, dy, 99) == expectedResult)
    assert(imIn == imIn_copy)

    print("Passed!")

def testVolSlicer():
    print("Testing volSlicer()...")

    vol = createDecimalVolume(4, 4, 4)
    vol_copy = copy.deepcopy(vol)
    # print3dList(vol)
    # print()

    sliceAxis=0
    sliceIndex=0
    expectedResult=[
            [0, 10, 20, 30],
            [100, 110, 120, 130],
            [200, 210, 220, 230],
            [300, 310, 320, 330],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=0
    sliceIndex=1
    expectedResult=[
            [1, 11, 21, 31],
            [101, 111, 121, 131],
            [201, 211, 221, 231],
            [301, 311, 321, 331],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=0
    sliceIndex=2
    expectedResult=[
            [2, 12, 22, 32],
            [102, 112, 122, 132],
            [202, 212, 222, 232],
            [302, 312, 322, 332],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=0
    sliceIndex=3
    expectedResult=[
            [3, 13, 23, 33],
            [103, 113, 123, 133],
            [203, 213, 223, 233],
            [303, 313, 323, 333],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=1
    sliceIndex=0
    expectedResult=[
            [0, 1, 2, 3],
            [100, 101, 102, 103],
            [200, 201, 202, 203],
            [300, 301, 302, 303],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=1
    sliceIndex=1
    expectedResult=[
            [10, 11, 12, 13],
            [110, 111, 112, 113],
            [210, 211, 212, 213],
            [310, 311, 312, 313],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=1
    sliceIndex=2
    expectedResult=[
            [20, 21, 22, 23],
            [120, 121, 122, 123],
            [220, 221, 222, 223],
            [320, 321, 322, 323],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=1
    sliceIndex=3
    expectedResult=[
            [30, 31, 32, 33],
            [130, 131, 132, 133],
            [230, 231, 232, 233],
            [330, 331, 332, 333],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=2
    sliceIndex=0
    expectedResult=[
            [0, 1, 2, 3],
            [10, 11, 12, 13],
            [20, 21, 22, 23],
            [30, 31, 32, 33],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=2
    sliceIndex=1
    expectedResult=[
            [100, 101, 102, 103],
            [110, 111, 112, 113],
            [120, 121, 122, 123],
            [130, 131, 132, 133],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=2
    sliceIndex=2
    expectedResult=[
            [200, 201, 202, 203],
            [210, 211, 212, 213],
            [220, 221, 222, 223],
            [230, 231, 232, 233],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=2
    sliceIndex=3
    expectedResult=[
            [300, 301, 302, 303],
            [310, 311, 312, 313],
            [320, 321, 322, 323],
            [330, 331, 332, 333],
        ]
    assert(volSlicer(vol, sliceIndex, sliceAxis) == expectedResult)
    assert(vol == vol_copy)
    
    print("Passed!")

def testMaximumIntensityProjection():
    print("Testing maximumIntensityProjection()...")

    vol = createDecimalVolume(4, 4, 4)
    vol[1][2][1] = 999
    vol_copy = copy.deepcopy(vol)
    # print3dList(vol)
    # print()

    sliceAxis=0
    expectedResult=[
            [3, 13, 23, 33],
            [103, 113, 999, 133],
            [203, 213, 223, 233],
            [303, 313, 323, 333],
        ]
    assert(maximumIntensityProjection(vol, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=1
    expectedResult=[
            [30, 31, 32, 33],
            [130, 999, 132, 133],
            [230, 231, 232, 233],
            [330, 331, 332, 333],
        ]
    assert(maximumIntensityProjection(vol, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    sliceAxis=2
    expectedResult=[
            [300, 301, 302, 303],
            [310, 311, 312, 313],
            [320, 999, 322, 323],
            [330, 331, 332, 333],
        ]
    assert(maximumIntensityProjection(vol, sliceAxis) == expectedResult)
    assert(vol == vol_copy)

    print("Passed!")

def testMakeWordSearch():
    print("Testing makeWordSearch()...")
    board = makeWordSearch([], False)
    assert(board == None)

    board = makeWordSearch(["ab"], False)
    assert(board == [['a', 'b'], ['-', '-'] ])
    board = makeWordSearch(["ab"], True)
    assert(board == [['a', 'b'], ['c', 'd'] ])
    board = makeWordSearch(["ab", "bc", "cd"], False)
    assert(board == [['a', 'b'], ['c', 'd'] ])
    board = makeWordSearch(["ab", "bc", "cd", "de"], False)
    assert(board == [['a', 'b', '-'], ['c', 'd', '-'], ['d', 'e', '-']])
    board = makeWordSearch(["ab", "bc", "cd", "de"], True)
    assert(board == [['a', 'b', 'a'], ['c', 'd', 'c'], ['d', 'e', 'a']])

    board = makeWordSearch(["abc"], False)
    assert(board == [['a', 'b', 'c'], ['-', '-', '-'], ['-', '-', '-']])
    board = makeWordSearch(["abc"], True)
    assert(board == [['a', 'b', 'c'], ['c', 'd', 'a'], ['a', 'b', 'c']])

    board = makeWordSearch(["abc", "adc", "bd", "bef", "gfc"], False)
    assert(board == [['a', 'b', 'c'], ['d', 'e', '-'], ['c', 'f', 'g']])
    board = makeWordSearch(["abc", "adc", "bd", "bef", "gfc"], True)
    assert(board == [['a', 'b', 'c'], ['d', 'e', 'a'], ['c', 'f', 'g']])

    board = makeWordSearch(["abcd", "abc", "dcb"], False)
    assert(board == [['a', 'b', 'c', 'd'],
                     ['-', '-', '-', '-'], 
                     ['-', '-', '-', '-'],
                     ['-', '-', '-', '-']])
    board = makeWordSearch(["abcd", "abc", "dcb", "xa", "bya"], False)
    assert(board == [['a', 'b', 'c', 'd'],
                     ['x', 'y', '-', '-'], 
                     ['-', 'a', '-', '-'],
                     ['-', '-', '-', '-']])
    board = makeWordSearch(["abcd", "abc", "dcb", "xa", "bya", "bax", "dca"],
                           False)
    assert(board == [['a', 'b', 'c', 'd'],
                     ['x', 'y', 'c', '-'], 
                     ['-', 'a', '-', '-'],
                     ['-', '-', 'b', '-']])
    board = makeWordSearch(["abcd", "abc", "dcb", "xa", "bya", "bax", "dca"],
                           True)
    assert(board == [['a', 'b', 'c', 'd'],
                     ['x', 'y', 'c', 'a'], 
                     ['b', 'a', 'd', 'e'],
                     ['c', 'e', 'b', 'a']])

    print("Passed!")

#################################################
# testAll and main
#################################################

def testAll():
    # comment out the tests you do not wish to run!

    testNondestructiveRemoveRowAndCol()
    testDestructiveRemoveRowAndCol()
    testIsKingsTour()
    testIsMagicSquare()
    testWordSearchWithIntegerWildcards()
    testPanImage()
    testVolSlicer()
    testMaximumIntensityProjection()

    # Bonus:
    # testMakeWordSearch()

def main():
    cs112_f22_week5_linter.lint()
    testAll()
    startViewer()

if __name__ == '__main__':
    main()
