1

Simulate a liquid flow through a randomly created square matrix that contains a set of integers from 0-9 using Python. The liquid should start from the top left corner of the matrix. It could only move towards right or below adjacent matrix. The lower value of the adjacent matrix, the higher potential for it to flow. In the case of the right and below adjacent matrix have the same value, the program should be able to simulate both conditions (Refer attached example image). The movement of the liquid is considered stop at the right or below edge of the matrix. The program should be able to show the sum of all numbers that the liquid has pass through, in all possibilities and visualize the path. Visualization can be done by replacing the numbers lying in the path with asterisk (*) , vertical line/pipe symbol (|), hyphen (-) or other relevant symbols such as (~,>= etc). Other methods to visualize can be accepted. Example output should look like this: Example output visualization

This is what i have coded so far, but it does not output all the possible outcomes and the sum of integers in which the liquid flows through;

import random
import numpy as np
import copy

a=[]
n=int(input("Enter matrix size between 8 to 20 only= "))

while True:
    if n<8:
        n=int(input("Input size less than 8! Enter at least 8 or more = "))
    elif n>20:
        n=int(input("Input size more than 20! Enter at most 20 or lesser = "))
    else:
        break

print()
print(f'{n} × {n} matrix generated from random numbers of 0-9 :\n')

def create_matrix(n, a):
    for i in range (n):
        v=[]
        for j in range (n):
            v.append(random.randint(0,9))
        a.append(v)
my.create_matrix(n, a)
b=copy.deepcopy(a)
#c=copy.deepcopy(a)

def print_array(n, array):
    for i in range(n):
        for j in range(n):
            print(array[i][j], end=" ")
        print()
    print()

my.print_array(n, a)

def move_right(b, i, j, n):
        b[0][0]="."
        while i+1 < n and j+1<n :
            if b[i+1][j] < b[i][j+1]:
                b[i+1][j]="."
                i+=1
            elif b[i][j+1] < b[i+1][j]:
                b[i][j+1]="."
                j+=1
            elif b[i+1][j] == b[i][j+1]:
                b[i][j]="*"
                #c=copy.deepcopy(b)
                #move_bottom(c, i, j, n)
                b[i][j+1]="."
                j+=1
            else:
                break

def move_bottom(array,i ,j, n):
    array[i][j]="."
    alt=0
    while i+1 < n and j+1<n :
        if array[i+1][j] < array[i][j+1]:
            array[i+1][j]="."
            i+=1
        elif array[i][j+1] < array[i+1][j]:
            array[i][j+1]="."
            j+=1
        elif array[i+1][j] == array[i][j+1]:
            array[i][j]="*"
            bb=copy.deepcopy(array)
            move_right(bb,i,j,n)
            array[i+1][j]="."
            i+=1
            alt+=1
        else:
            break
    print_array(n, array)

my.move_bottom(b, 0, 0, n)

I really need help with my coding so that i can output all the possible outcomes that the liquid can flow and the sum of integers in which the liquid flows through. If there's any other way to easily code this program using python given the conditions, please let me know!

Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
Merlin
  • 11
  • 4
  • 1
    Why the liquid tag? Why the flow tag? **Read** tag descriptions before adding them! – Patrick Artner Aug 07 '20 at 08:24
  • 1
    Mistakenly.. but i think the ques is clear :) – Merlin Aug 07 '20 at 08:25
  • 1
    @Merlin People _watch_ tags that they have interest in. Why should 500000 users of SO have to click on this question because they watch the C# tag if nothing in this question could be of interest for them? Using wrong tags is a giant waste of time other human beings. – Patrick Artner Aug 07 '20 at 09:09
  • @PatrickArtner Noted sir, thank you! ;) – Merlin Aug 07 '20 at 14:53
  • Please don't make more work for other people by vandalizing your posts. By posting on the Stack Exchange network, you've granted a non-revocable right, under the [CC BY-SA 4.0 license](//creativecommons.org/licenses/by-sa/4.0/), for Stack Exchange to distribute that content (i.e. regardless of your future choices). By Stack Exchange policy, the non-vandalized version of the post is the one which is distributed. Thus, any vandalism will be reverted. If you want to know more about deleting a post please see: [How does deleting work?](//meta.stackexchange.com/q/5221). – Patrick Artner Aug 12 '20 at 13:19
  • Removing 90% of your question including the code you did to try to solve this makes this question OFFTOPIC and I would have never answered. Somehow my answer now got a -5 out of the blue - maybe because someone thought I answered an offtopic question. – Patrick Artner Aug 12 '20 at 13:19

1 Answers1

1

Here is how you could do this for all possible ways.

A map of

456
867
978

would be represented as dictionary of dictionaries:

{ (0,0): {(1,0): {(2,0): {}, 
                  (1,1): { (2,1): {}, 
                           (1,2): {}}}}

and can be used to generate all paths from it.

Then you need a copy of the original numbers and can add the ascii art instead of the numbers at the correct positions. The total map has to check if you add another way to a formerly already set position, and if so replace it with a "both-ways" marker.


Some utility-methods:

import random
import copy

random.seed(42)  # fixed for repeatability

class Consts:
    """Some constants to use throughout"""
    VALUES = range(10)
    SIZE = 8
    START = (0,0)
    OUT_OF_BOUNDS = 99

def generate():
    """Generates an Consts.SIZE * Consts.SIZE list of lists of numbers 0-9"""
    n = Consts.SIZE
    data = random.choices(Consts.VALUES, k = n*n)
    return [data[i * n : i * n + n] for i in range(n)]

def v(data, x, y):
    """Returns the value in data at position x,y or
    Consts.OUT_OF_BOUNDS if out of bounds."""
    try:
        return data[y][x]
    except:
        return Consts.OUT_OF_BOUNDS

def peek_east(data, pos):
    """Returs the value, position tuple of the position one to the east"""
    new_pos = pos[0] + 1, pos[1]
    return v(data, *new_pos), new_pos

def peek_south(data, pos):
    """Returs the value, position tuple of the position one to the south"""
    new_pos = pos[0], pos[1] + 1
    return v(data, *new_pos), new_pos
  
def done(pos):
    """Returns True if a position is at the border of the map"""
    return Consts.SIZE-1 in pos

def pp(arr):
    """Pretty print a map / list of lists"""
    print('\n'.join(''.join(map(str, n)) for n in arr))

the exploration part:

def explore(data, start=None, ways=None):
    """Creates/Explores all the ways. The exploration position are stored 
    as dictionary that contains all explored tiles as a dict of dict of ...
    of possible ways to go through the map."""
    size = Consts.SIZE
    OUT = Consts.OUT_OF_BOUNDS
    
    start = start or Consts.START
    ways = ways or {}

    pos = start
    if done(pos):
        ways[pos] = "DONE"
        return 

    routes = []
    # get east and south data to see where we can go from here
    east, east_pos = peek_east(data, pos)
    south, south_pos = peek_south(data, pos)

    # where to move to
    if east <= south:
        routes.append(east_pos)
    if east >= south:
        routes.append(south_pos)

    # add the visited tiles and the empty dicts for them to ways
    for way in routes:
        if pos not in ways:
            ways[pos] = {}
        if way not in ways:
            ways[way] = {}
        ways[pos][way] = ways[way]

    # explore further
    for way in routes:
        explore(data, way, ways)

    # simplify dict, only return the (0,0) element
    return {Consts.START: ways[Consts.START]}

How to use it & ascii art:

array = generate()

pp(array)
exp = explore(array)
 
# used to create all possible paths from the dict we generated
def get_paths(tree, cur=()):
    """ Source:  https://stackoverflow.com/a/11570745/7505395  """
    if not tree or tree == "DONE":
        yield cur
    else:
        for n, s in tree.items():
            for path in get_paths(s, cur+(n,)):
                yield path

p = list(get_paths(exp))

# copy the original map for a "all in one" map
d_all = copy.deepcopy(array)

for path in p:
    # copy the original map for this paths map
    d = copy.deepcopy(array)
    # get from,to pairs from this runway
    for (_from, _to) in zip(path, path[1:]):
        _, pe = peek_east(array, _from)
        _, ps = peek_south(array, _from)

        # ASCII Art the map
        if _to == pe:
          d[_from[1]][_from[0]] = "-" 
          d_all[_from[1]][_from[0]] = "-" if isinstance(d_all[_from[1]][_from[0]], int) else  ("-" if d_all[_from[1]][_from[0]] == "-" else "+")
        else:
          d[_from[1]][_from[0]] = "|"
          d_all[_from[1]][_from[0]] = "|" if isinstance(d_all[_from[1]][_from[0]], int) else ("|" if d_all[_from[1]][_from[0]] == "|" else "+")

    # ASCII Art the last one
    d[_to[1]][_to[0]] = "°"
    d_all[_to[1]][_to[0]] = "°"

    # print this map
    print("\nPath: ", path)
    pp(d) 

# and total map
print("\nTotal mapping")
pp(d_all)

Output:

60227680
40250165
25808631
93008687
59358685
70220212
63322966
17139656

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), 
        (6, 2), (7, 2))
-|227680
4-----|5
258086-°
93008687
59358685
70220212
63322966
17139656

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (5, 2), 
        (6, 2), (7, 2))
-|227680
4----|65
25808--°
93008687
59358685
70220212
63322966
17139656

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4), 
        (3, 5), (4, 5), (5, 5), (6, 5), (7, 5))
-|227680
4--|0165
258|8631
930|8687
593|8685
702----°
63322966
17139656

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4), 
        (3, 5), (4, 5), (4, 6), (5, 6), (6, 6), (6, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332--|6
171396°6

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4), 
        (3, 5), (4, 5), (4, 6), (5, 6), (5, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332-|66
17139°56

Path:  ((0, 0), (1, 0), (1, 1), (2, 1), (3, 1), (3, 2), (3, 3), (3, 4), 
        (3, 5), (4, 5), (4, 6), (4, 7))
-|227680
4--|0165
258|8631
930|8687
593|8685
702-|212
6332|966
1713°656

Total mapping
-|227680
4--+-+|5
258|8--°
930|8687
593|8685
702-+--°
6332++|6
1713°°°6
Patrick Artner
  • 50,409
  • 9
  • 43
  • 69
  • Dear downvoter - please tell me why you downvoted. The question was, when I answered it, far more sohisticated then the late edit wich removed about 95% of the question (including code that was tried by the OP). I reverted to the more substantial question-revision. – Patrick Artner Aug 12 '20 at 13:20