Register a SA Forums Account here!
JOINING THE SA FORUMS WILL REMOVE THIS BIG AD, THE ANNOYING UNDERLINED ADS, AND STUPID INTERSTITIAL ADS!!!

You can: log in, read the tech support FAQ, or request your lost password. This dumb message (and those ads) will appear on every screen until you register! Get rid of this crap by registering your own SA Forums Account and joining roughly 150,000 Goons, for the one-time price of $9.95! We charge money because it costs us money per month for bills, and since we don't believe in showing ads to our users, we try to make the money back through forum registrations.
 
  • Locked thread
Third
Sep 9, 2004
The most noble title any child can have.
neighbors is a list of elements that either have the value True or False, so that won't work.

Also, if you give the function coordinates of a cell on the edge of the grid it will just raise the KeyError instead of giving you the neighbors.

Adbot
ADBOT LOVES YOU

breaks
May 12, 2001

Dictionaries are mutable; you can add things to and remove things from them without creating a new dictionary. But in neighbors you are working with a list, rather than a dictionary. :) Lists are also mutable, though!

But you can avoid the problem of removing it after the fact by not putting it there in the first place. You can actually put an if into a comprehension:

code:
print [i for i in range(0,10) if i != 5]
A warning: two fors and an if is about the most you will ever want to put into a single comprehension. Even that may sometimes be too much. Don't let them become a source of evil. If you ever start to feel like a comprehension might be too long or tough to read or understand, rewrite it as either for loops, or multiple separate comprehensions.

A couple other comments:

Why do you assign xcoord and ycoord to x and y instead of writing (for example) "def togglecell(x, y):"?

This may be a bit picky, but avoid quit() and use sys.exit() instead. quit() is intended only for use in the interactive interpreter. It is a flaw of Python that it works at all outside of it.

If you are interested, a suggested next feature: write a function that returns a range-like list clipped to the size of your grid. It can call range itself, if you want. In other words:

code:
def my_range(start, end):
   # implement me

print my_range(-1, 3)
# prints [0, 1, 2]
print my_range(8, 11)
# prints [8, 9]

QuarkJets
Sep 8, 2008

Dominoes posted:

ie: Python's a language for programmers?

What are some high-level languages better suited to making applications, that are similar to Python? C#?

I disagree; C#, Java, and other languages require you to install a run-time environment of some sort, just like Python. Even Visual Studio C++ programs often require you to install something in order to get them to run on a fresh Windows machine. Windows applications that don't require some sort of installation are the exception, not the norm.

Dominoes posted:

I doubt anyone reading this post would be able to run any python program I made on their Ubuntu computer without a bit of legwork. Imagine asking a non-programer to do that.

Why not? If you've created an RPM or something for them to install then it should be as simple as
code:
python Dominoes_app
or
code:
Dominoes_app
if it has a shebang. If it's on their desktop and you included a python shebang at the top of it then you can give it to them and they can double-click on it and it will run because Python is included with Ubuntu. The only trick is making sure that anything you wrote that needs to be imported is in their PYTHONPATH, which is why you build a package that installs your scripts somewhere sensible; that way your user doesn't need to think about it.

e: When your installation instructions are "type this command into your terminal" then you can't really accuse your users of needing to know anything. This doesn't mean that you need to build a binary, just a package

QuarkJets fucked around with this message at 07:40 on Sep 28, 2013

Suspicious Dish
Sep 24, 2011

2020 is the year of linux on the desktop, bro
Fun Shoe

Pollyanna posted:

Python code:
    neighbors = [ the_world[(i, j)] for i in range (x-1, x+2) for j in range (y-1, y+2)]

Note that if your X/Y coordinates here is 0, it will wrap around to the other side, which may not be what you want. It's also a bit complicated compared to how I would write it.

(also, stop mixing tabs and spaces)

SYSV Fanfic
Sep 9, 2003

by Pragmatica
I wrote a script to synchronize two folders, mostly to use for games that don't support steam cloud. It works in conjunction with google drive. I needed to create all sub folders for a full file path. This is what I came up with. Is there a better, less C, more python way to accomplish this?

NtotheTC
Dec 31, 2007


If you wanted it to be cross platform you could make use of os.path.join and similar utils rather than manually concatenating dir strings

Dominoes
Sep 20, 2007

QuarkJets posted:

Why not? If you've created an RPM or something for them to install then it should be as simple as
code:
python Dominoes_app
or
code:
Dominoes_app
if it has a shebang. If it's on their desktop and you included a python shebang at the top of it then you can give it to them and they can double-click on it and it will run because Python is included with Ubuntu. The only trick is making sure that anything you wrote that needs to be imported is in their PYTHONPATH, which is why you build a package that installs your scripts somewhere sensible; that way your user doesn't need to think about it.

e: When your installation instructions are "type this command into your terminal" then you can't really accuse your users of needing to know anything. This doesn't mean that you need to build a binary, just a package
Didn't know if worked that way - I assumed RPMs were for binaries. So for example, it might install Python 3.3, Pyqt5, a customized version of Pypdf that has bugfixes, an unofficial dev build of oathlib, the appropriate scipy modules etc? When uninstalled, would it clean up the packages it left scattered across the hard drive/python install? Or would it all be packaged together in the RPM?

Dominoes fucked around with this message at 15:53 on Sep 28, 2013

QuarkJets
Sep 8, 2008

Dominoes posted:

Didn't know if worked that way - I assumed RPMs were for binaries. So for example, it might install Python 3.3, Pyqt5, a customized version of Pypdf that has bugfixes, an unofficial dev build of oathlib, the appropriate scipy modules etc? When uninstalled, would it clean up the packages it left scattered across the hard drive/python install? Or would it all be packaged together in the RPM?

I think that's possible, but probably not what you'd want to do. Instead, you can list those as your dependencies, and when someone tries to install your RPM they'll be asked if they want to install those dependencies (which would be downloaded from somewhere else).

Have a look at this:
http://docs.python.org/2/distutils/builtdist.html

Pollyanna
Mar 5, 2005

Milk's on them.


Okay, what was I doing again?

Python code:
def neighbors(x, y):

    try:
        neighbors = [the_world[(i, j)] for i in range(x - 1, x + 2) for j in range(y - 1, y + 2)]
        print neighbors
        neighbors = neighbors - the_world[(x, y)]
        print neighbors
        return neighbors
    except KeyError:
        print "Out of range."
This piece of code is still a pain in the rear end. I've forgotten what that line after try: is supposed to do :suicide:

Right, I remember. I want to iterate over the dictionary the_world and return the values of all cells directly next to the queried cell, NOT including the queried cell itself. Unfortunately, subtracting a bool from a list doesn't work (obviously). Maybe I want a way to tell that mess that defines neighbors to skip over the_world[(x, y)].

Forgive me if I've asked this already. Is there a simpler way of doing that [the_world[(i, j)] for i in range(x - 1, x + 2) for j in range(y - 1, y + 2)] poo poo? Nested for loops or something?

edit: itertools.product works for this.

Python code:
def neighbors(x, y):

    try:
        neighbors = []
        for i, j in it.product(range(x - 1, x + 2), range(y - 1, y + 2)):
                if (i, j) != (x, y):
                    neighbors.append(the_world[(i,j)])
        print neighbors
        return neighbors
    except KeyError:
        print "Out of range."
I'm realizing that the KeyError exception needs to return something. What's happening is that neighbors(x,y) tries to check the neighbors of target cell (x, y). This creates a problem if, for example, the cell is (0, 0). That makes the ranges both (-1, 2). I'm not sure that works correctly.

Pollyanna fucked around with this message at 06:44 on Sep 29, 2013

fritz
Jul 26, 2003

Pollyanna posted:

Unfortunately, the same thing that makes Python common in that field means that there's not much support for anything that doesn't do what a bioinformatician or computational biologist wants (i.e. anything beyond scripting and automation).

Kinda sad. Hopefully Python will become more popular for other problem fields in the future.

Not really sure I'm following you here.

Third
Sep 9, 2004
The most noble title any child can have.

Pollyanna posted:

Forgive me if I've asked this already. Is there a simpler way of doing that [the_world[(i, j)] for i in range(x - 1, x + 2) for j in range(y - 1, y + 2)] poo poo? Nested for loops or something?

Python code:
neighbors = []

for i in range(x-1, x+2):
    for j in range(y-1, y+2):
        if i != x or j != y:
            neighbors.append(the_world[(i, j)])
Still doesn't fix your edge cell problem though.

Pollyanna
Mar 5, 2005

Milk's on them.


fritz posted:

Not really sure I'm following you here.

Ignore that post. I talk like I know what I'm talking about.

returnh posted:

Python code:
neighbors = []

for i in range(x-1, x+2):
    for j in range(y-1, y+2):
        if i != x or j != y:
            neighbors.append(the_world[(i, j)])
Still doesn't fix your edge cell problem though.

Sorry, I just updated my code :shobon: I'm still trying to figure out the whole edge problem right now, though.

Python code:
def neighbors(x, y):

    try:
        neighbors = []
        for i, j in it.product(range(x - 1, x + 2), range(y - 1, y + 2)):
                if (i, j) != (x, y):
                    neighbors.append(the_world[(i,j)])
        print neighbors
        return neighbors
    except KeyError:
        print "Out of range."

Pollyanna
Mar 5, 2005

Milk's on them.


I want to tell it to ignore any (i,j) that isn't present in the_world, like (-1, -1). So that means checking to see if there is a the_world[(i, j)]. I'll try moving the try: into the if statement.

Python code:
def neighbors(x, y):

    neighbors = []
    for i, j in it.product(range(x - 1, x + 2), range(y - 1, y + 2)):
        if (i, j) != (x, y):
            try:
                neighbors.append(the_world[(i,j)])
            except KeyError:
                pass # if the_world[(i, j)] does not exist, skip it.
    return neighbors


def checkneighbors(x, y):

    live_neighbors = neighbors(x, y).count(True)

    if live_neighbors < 2:
        the_world[x, y] = False
        print str((x,y)) + " died."
    elif live_neighbors > 3:
        the_world[x, y] = False
        print str((x,y)) + " died."
    elif live_neighbors == 2:
        the_world[x, y] = True
        print str((x,y)) + " lived!"
    elif live_neighbors == 3:
        the_world[x, y] = True
        print str((x,y)) + " lived!"
    else:
        print "Something went wrong!"
It works! Sort of. It still has the edge runoff problem, where cells like (9, 9) have ranges (8, 11). I think that's loving with the logic. I can have the code restrict i and j from being larger than (width - 1), I guess.

Pollyanna fucked around with this message at 07:01 on Sep 29, 2013

suffix
Jul 27, 2013

Wheeee!
You could use dict.get() with a default value for nonexisting cells.
Python code:
neighbors.append(the_world.get((i,j), False))

Pollyanna
Mar 5, 2005

Milk's on them.


Okay, this should be a (sorta) working version of Conway's Game of Life:

http://pastebin.com/7b8Zt6kf

It asks for a height and width, and currently doesn't toggle any cells. That comes once I figure out how to give it a GUI.

QuarkJets
Sep 8, 2008

Pollyanna posted:

Okay, this should be a (sorta) working version of Conway's Game of Life:

http://pastebin.com/7b8Zt6kf

It asks for a height and width, and currently doesn't toggle any cells. That comes once I figure out how to give it a GUI.

Overall the code looks good, but I have suggestions:

Instead of doing things like if the_world[x, y] == False: you should do if not the_world[x, y]:. Likewise, when you check for whether something is True, do if the_world[x,y]

Are you sure that you want to use & instead of and? They are different and it looks like you mean to use and, but I'm not sure

I had never heard of itertools before! What a neat tool. However, appending to a list is still a pretty slow operation compared to generating a list. itertools.product is creating a generator, so you can do this in a single line (broken up for readability):

Python code:
neighbors = [(i,j) for i,j in it.product(range(x-1,x+2), range(y-1,y+2)) \
		if i != x and j != y \
		and i >= 0 and j >= 0 \
		and i < wide - 1 and j < high - 1 ]

Symbolic Butt
Mar 22, 2009

(_!_)
Buglord
To each his own but if I wanted to use a list comprehension here (lots of people complain about them looking terse) I would've done this way:

Python code:
def neighbors(x, y):
    return [(i, j)
            for i in xrange(x-1, x+2)
            if i != x
            if 0 <= i < wide-1
            for j in xrange(y-1, y+2)
            if j != y
            if 0 <= j < high-1]
or using the (x)range third argument:

Python code:
def neighbors(x, y):
    return [(i, j)
            for i in xrange(x-1, x+2, 2)
            if 0 <= i < wide-1
            for j in xrange(y-1, y+2, 2)
            if 0 <= j < high-1]
I believe with these ifs in the right places it's a little faster than using itertools.product, not that it matters much.

I really don't like using those pesky \ line breakers. I love these 0 < x < 1 math inequation style in python, too bad you don't get those in numpy. :(

If you want some spoilers, tef made a game of life in a few lines some time ago, I thought it was really well written and I learned a lot back then just looking at it: http://forums.somethingawful.com/showthread.php?threadid=3376083&pagenumber=52&perpage=40#post405420314

Nippashish
Nov 2, 2005

Let me see you dance!

Pollyanna posted:

Okay, this should be a (sorta) working version of Conway's Game of Life:

http://pastebin.com/7b8Zt6kf

It asks for a height and width, and currently doesn't toggle any cells. That comes once I figure out how to give it a GUI.

This should all probably be packaged up into a class so things like high and wide and the_world become member variables instead of globals, but this is orthogonal to my comments below.

Your code doesn't actually implement the correct rules for the game of life as described on the wiki page. I don't know if this is intentional or not.

You should name togglecell and checkneighbors as toggle_cell and check_neighbors respectively, to match the rest of your code and normal python convention.

neighbors would be clearer if you generate a list of all neighbors and then prune out the ones outside the grid like so (pretty formatting optional):
code:
def neighbors(x, y):
    my_neighbors = [
        (x-1, y-1), (x, y-1), (x+1, y-1),
        (x-1, y)              (x+1, y),
        (x-1, y+1), (x, y+1), (x+1, y+1),
        ]
    return [ (i,j) for i,j in my_neighbors if 0 <= i < wide and 0 <= j < high ]
You shouldn't call a variable neighbors inside a function called neighbors. Confusing things would start happening if you ever tried to call neighbors recursively.

toggle_cell can be simpler (it also isn't used anywhere):
code:
def toggle_cell(x, y):
    the_world[x,y] = not the_world[x,y]
next_round could just be replaced with the_world = updated_world unless you have some plan for why you need two copies of the updated world around.

check_neighbors is badly named for what it does. It really figures out what the location x,y should look like at the next time step.

I think a better structure would be to have a function like advance_time which would move your world one step into the future like this:
code:
the_world = advance_time(the_world)
The advance_time function takes a world dictionary as an argument and return a new world dictionary that represents the state of the world one time step in the future. You can implement this function in terms of a next_state function that works like this:
code:
new_world[x,y] = next_state(the_world, x, y)
The next_state function takes a world and a position and returns the state of the cell at the given position one time step in the future.

I know other people in the thread told you to use a dictionary to represent the world, but I think this is a really absurd data structure to represent a dense matrix. A 2d numpy array of booleans or integers is a much more sane choice imo.

salisbury shake
Dec 27, 2011
http://pastebin.com/NDpuYbTG

According to ~my tests~ this should work if you like accessing points through list-indices. A view needs to be implemented that will display the state of the Universe instance between each Universe.tick() call.

Pollyanna
Mar 5, 2005

Milk's on them.


QuarkJets posted:

Overall the code looks good, but I have suggestions:

Instead of doing things like if the_world[x, y] == False: you should do if not the_world[x, y]:. Likewise, when you check for whether something is True, do if the_world[x,y]

Are you sure that you want to use & instead of and? They are different and it looks like you mean to use and, but I'm not sure

Thanks for bringing this up, PyCharm helped me clean up the logic and it looks a lot nicer now :)

Python code:
    for i, j in it.product(range(x - 1, x + 2), range(y - 1, y + 2)):
        if 0 <= i <= (wide - 1) and 0 <= j <= (high - 1) and (i, j) != (x, y):
            try:
                ...
Also I don't know what the hell is up with tabs vs spaces vs whatever. I use tabs in PyCharm and copying the code changes them to spaces or something.

itertools is pretty great. Means I don't have to write new or complicated code.

As for the list-append stuff, I'm still considering changing the data structure to an array. It seems a lot more obvious to me and I can easily change it into a picture if I want. Speaking of arrays...

Nippashish posted:

This should all probably be packaged up into a class so things like high and wide and the_world become member variables instead of globals, but this is orthogonal to my comments below.

Your code doesn't actually implement the correct rules for the game of life as described on the wiki page. I don't know if this is intentional or not.

Howso? I think I got the logic behind whether a cell lives or dies right - ...wait poo poo, I see it.

Python code:
    if live_neighbors < 2:
        updated_world.update({(x, y): False})  # Any live cell with fewer than two live neighbors dies, as if caused by under-population. (same logic if dead or alive)
    elif live_neighbors > 3:  # Any live cell with more than three live neighbors dies, as if by overcrowding. (same logic if dead or alive)
        updated_world.update({(x, y): False})
    elif live_neighbors == 2:  # Any live cell with two or three live neighbors lives on to the next generation. (different logic if dead or alive)
        if the_world[(x, y)] == True:
            updated_world.update({(x, y): True})
    elif live_neighbors == 3:  # Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. (same logic if dead or alive)
        updated_world.update({(x, y): True})
I had both dead and alive cells live as a result of having two neighbors. That should only work for live cells. :shobon: That should be correct now, right?

quote:

You should name togglecell and checkneighbors as toggle_cell and check_neighbors respectively, to match the rest of your code and normal python convention.

Done.

quote:

neighbors would be clearer if you generate a list of all neighbors and then prune out the ones outside the grid like so (pretty formatting optional):
code:
def neighbors(x, y):
    my_neighbors = [
        (x-1, y-1), (x, y-1), (x+1, y-1),
        (x-1, y)              (x+1, y),
        (x-1, y+1), (x, y+1), (x+1, y+1),
        ]
    return [ (i,j) for i,j in my_neighbors if 0 <= i < wide and 0 <= j < high ]

Wow, that's way more elegant. I got the code working and it looks like it runs way faster.

quote:

You shouldn't call a variable neighbors inside a function called neighbors. Confusing things would start happening if you ever tried to call neighbors recursively.

Right, PyCharm has been yelling at me over that. I think I've fixed all instances of it now.

quote:

toggle_cell can be simpler (it also isn't used anywhere):
code:
def toggle_cell(x, y):
    the_world[x,y] = not the_world[x,y]

Yeah, that's an artifact of some other thing that didn't pan out. I removed it.

quote:

next_round could just be replaced with the_world = updated_world unless you have some plan for why you need two copies of the updated world around.

Good point. Fixed.

quote:

check_neighbors is badly named for what it does. It really figures out what the location x,y should look like at the next time step.

I renamed it "tick", so it'll return what the gameboard will look like next round (or "tick").

quote:

I think a better structure would be to have a function like advance_time which would move your world one step into the future like this:
code:
the_world = advance_time(the_world)
The advance_time function takes a world dictionary as an argument and return a new world dictionary that represents the state of the world one time step in the future. You can implement this function in terms of a next_state function that works like this:
code:
new_world[x,y] = next_state(the_world, x, y)
The next_state function takes a world and a position and returns the state of the cell at the given position one time step in the future.

I think this is what I was trying to do with next_round, but it didn't work very well.

quote:

I know other people in the thread told you to use a dictionary to represent the world, but I think this is a really absurd data structure to represent a dense matrix. A 2d numpy array of booleans or integers is a much more sane choice imo.

Yeah, I kinda want to change this over to an array instead. I'm used to matrices and I want to output this as an image eventually, so a matrix (array) makes the most sense to me. I'll need to figure out how to make this logic work with arrays instead, though, since it requires certain changes.

As for the class wrapper, let me get back to that later.

Symbolic Butt posted:

If you want some spoilers, tef made a game of life in a few lines some time ago, I thought it was really well written and I learned a lot back then just looking at it: http://forums.somethingawful.com/showthread.php?threadid=3376083&pagenumber=52&perpage=40#post405420314

gently caress :negative:

What I'm doing is different from trying to reinvent the wheel. I'm writing this as programming practice. But reading that definitely shows I have a ways to go.

Here's what I got so far:

http://pastebin.com/ikbZj2xN

edit: :byodood: oh my god arrays use the opposite x y notation that i've been using the entire time ffffffuckkkkkkkkkkkkkkkkkkkk

Well, it shouldn't matter though, right? As long as the range(wide) etc. stuff is the same.

Pollyanna fucked around with this message at 17:24 on Sep 29, 2013

fritz
Jul 26, 2003

suffix posted:

You could use dict.get() with a default value for nonexisting cells.
Python code:
neighbors.append(the_world.get((i,j), False))

If you're going to do that, maybe just use a collections.defaultdict instead.

Pollyanna
Mar 5, 2005

Milk's on them.


Okay, wait, something hosed up. Setting three contiguous cells as alive results in none of them living in the next round, while one of them should. What :psyduck: Forget it, I'm moving onto arrays.

Met48
Mar 15, 2009
Your neighbors function generates a list of valid neighbor xy pairs. You still need to get the True/False cell statuses from the_world before counting how many cells are alive.

Pollyanna
Mar 5, 2005

Milk's on them.


Okay, here's the array version:

http://pastebin.com/ikbZj2xN

Right now, it asks you for a height, width, and number of rounds. For testing this out, I input 5, 5, and 2. I have it print out the beforehand world, then set it equal to the after world, then print the beforehand world again. It should end up as:

Python code:
[[False False False False False]
 [False False False False False]
 [False  True  True  True False]
 [False False False False False]
 [False False False False False]]

[[False False False False False]
 [False False  True False False]
 [False False  True False False]
 [False False  True False False]
 [False False False False False]]

[[False False False False False]
 [False False False False False]
 [False  True  True  True False]
 [False False False False False]
 [False False False False False]]
But instead, it's:

Python code:
[[False False False False False]
 [False False False False False]
 [False  True  True  True False]
 [False False False False False]
 [False False False False False]]

[[False False False False False]
 [False False  True False False]
 [False False  True False False]
 [False False  True False False]
 [False False False False False]]

[[False False False False False]
 [False False False False False]
 [False False False False False]
 [False False False False False]
 [False False False False False]]
What gives? At the end of the first round, array_world should equal the second matrix there, which should come out at the end of round 2 as the same array as the very beginning. Instead, it lists all of them as false. Is this a local vs. global variable thing again?

I tried to use the second array as the beginning, but:

Python code:
[[False False False False False]
 [False False  True False False]
 [False False  True False False]
 [False False  True False False]
 [False False False False False]]


[[False False False False False]
 [False False False False False]
 [False  True  True  True False]
 [False False False False False]
 [False False False False False]]


[[False False False False False]
 [False False  True  True False]
 [False  True False  True False]
 [False False False False False]
 [False False False False False]]
:wtc: If I got the i,j poo poo mixed up again I'll scream. Wait, that can't be it, high and wide are the same number...

Python code:
[[False False False False False]
 [False False False False False]
 [False  True  True  True False]
 [False False False False False]
 [False False False False False]]
After this, the ones that should live are (counting from left to right, top to bottom) number 8, number 13, number 18. That creates a vertical line instead of a horizontal line. Instead, for some reason, it returns number 9 (right of number 8) as having three true neighbors, which is clearly wrong.

gently caress, I think what's happening is that the drat program is changing the array between checking each cell's neighbors. What the gently caress? Why? How come it works correctly once, but doesn't work the second time!?

edit: Oh god. I forgot to reset next_world. :downs: I'm retarded.

This is the whole "by value" versus "by reference" thing, right? How does that work?

Final version: http://pastebin.com/ikbZj2xN

Pollyanna fucked around with this message at 20:02 on Sep 29, 2013

Dominoes
Sep 20, 2007

Has anyone found an easy to way to package qt5 programs without the 17mb localization icudt49.dll ? Bloat. I've read you can compile a custom QT with a -no-icu tag, but has anyone here successfully done so?

Dominoes fucked around with this message at 19:33 on Sep 29, 2013

BeefofAges
Jun 5, 2004

Cry 'Havoc!', and let slip the cows of war.

Dominoes posted:

Has anyone found an easy to way to package qt5 programs without the 17mb localization icudt49.dll ? Bloat. I've read you can compile a custom QT with a -no-icu tag, but has anyone here successfully done so?

Have you tried zipping it after packaging? I'd expect that a localization file would be mostly plaintext and therefore highly compressible.

Dominoes
Sep 20, 2007

BeefofAges posted:

Have you tried zipping it after packaging? I'd expect that a localization file would be mostly plaintext and therefore highly compressible.
Yea - it goes down to about 6mb.

QuarkJets
Sep 8, 2008

Pollyanna posted:

Also I don't know what the hell is up with tabs vs spaces vs whatever. I use tabs in PyCharm and copying the code changes them to spaces or something.

Yeah, I don't think that SA uses tabs. Vim has an option for automatically converting tabs into a number of spaces, PyCharm probably does too; that would be my suggestion. I like it for exactly these types of situations (where copy-pasting code results in an tab vs space mismatch)

Pollyanna
Mar 5, 2005

Milk's on them.


QuarkJets posted:

Yeah, I don't think that SA uses tabs. Vim has an option for automatically converting tabs into a number of spaces, PyCharm probably does too; that would be my suggestion. I like it for exactly these types of situations (where copy-pasting code results in an tab vs space mismatch)

I actually think PyCharm automatically handles tabs and spaces and whatnot already, cause tabbing puts in four spaces for me. Easy enough.

Also, is there a tutorial for Tkinter out there that doesn't suck?

NtotheTC
Dec 31, 2007


There's an option in PyCharm settings to turn tabs into 4 spaces, pretty useful. Also, turn on punctuation, that way you can see what you've used. If for some ungodly reason you have to use tabs, at least be consistent.

NtotheTC fucked around with this message at 23:06 on Sep 29, 2013

Nippashish
Nov 2, 2005

Let me see you dance!

Pollyanna posted:

I renamed it "tick", so it'll return what the gameboard will look like next round (or "tick").
...
I think this is what I was trying to do with next_round, but it didn't work very well.

tick is a good name for that function.

Instead of having tick update the new board directly I recommend writing it so you use it like this:
code:
def tick(x, y, before):
    ...
    return next state for x,y (True or False)

...

next_world[x,y] = tick(x, y, array_world)
You should also probably have two tick functions, tick_world(before) and tick_position(x, y, before). tick_world is responsible for creating the new world and calling tick_position to fill in each cell and then returning the newly created world.

In general having functions create the values they return instead of accepting an argument that they mutate (like you do in tick now) will make your code a lot easier to reason about since you never need to worry that a function might change some data you give it.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

Pollyanna posted:

edit: :byodood: oh my god arrays use the opposite x y notation that i've been using the entire time ffffffuckkkkkkkkkkkkkkkkkkkk

Well, it shouldn't matter though, right? As long as the range(wide) etc. stuff is the same.

Don't be lazy about this. I once wrote an A* pathfinding implementation that had this exact same bug and it worked just fine for all my square grid tests. It wasn't until we tried making non square maps later that there was suddenly a really hard to find bug.

Master_Odin
Apr 15, 2010

My spear never misses its mark...

ladies
edit: type mismatch, whoops!

Dren
Jan 5, 2001

Pillbug

salisbury shake posted:

http://pastebin.com/NDpuYbTG

According to ~my tests~ this should work if you like accessing points through list-indices. A view needs to be implemented that will display the state of the Universe instance between each Universe.tick() call.

I mostly like this but I think you should move _step() and tick() out of your Universe class. That way Universe is a representation of the game board and there would be a clean separation between the model (Universe) and the business logic (_step() and tick()). Also, why is _step() a property?

Pie Colony
Dec 8, 2006
I AM SUCH A FUCKUP THAT I CAN'T EVEN POST IN AN E/N THREAD I STARTED
i'm trying to write haskell-style monads in python. doing something like this is straight-forward:

code:
def with_maybe():
    var('a') <- Maybe(lambda: mdiv(1, 0))
    mreturn(lambda: a)
make a var class with a __lt__ method, make a Maybe class with a __neg__ method, pass some contexts around, etc. but what i'd really like to be able to do is this:

code:
@do(Maybe)
def with_maybe():
    var('a') <- lambda: mdiv(1, 0)
    mreturn(lambda: a)
this requires (temporarily) setting the __neg__ method on FunctionType, which i can't quite figure out how to do. is this possible? (and yes i realize how horrible i am for wanting such a thing)

Symbolic Butt
Mar 22, 2009

(_!_)
Buglord
Messing around with numpy lately and I've been wondering if there's some way to make index arrays work like a defaultdict.

Python code:
import numpy as np

class defaultarray(np.ndarray):
    def __getitem__(self, index):
        try:
            return np.ndarray.__getitem__(self, index)
        except IndexError:
            return 0

a = np.array([10, 11, 12, 13])
b = a.view(defaultarray)

print(b[2])             # prints 12
print(b[6])             # prints 0
print(b[[3, 3, 1]])     # prints defaultarray([13, 13, 11]) as expected
print(b[[3, 3, 1, 5]])  # prints 0 instead of defaultarray([13, 13, 11, 0])
And so I'm seriously stuck here, I have no idea even where to look for how index arrays operates in these special methods.

Dren
Jan 5, 2001

Pillbug

Symbolic Butt posted:

Messing around with numpy lately and I've been wondering if there's some way to make index arrays work like a defaultdict.

Python code:
import numpy as np

class defaultarray(np.ndarray):
    def __getitem__(self, index):
        try:
            return np.ndarray.__getitem__(self, index)
        except IndexError:
            return 0

a = np.array([10, 11, 12, 13])
b = a.view(defaultarray)

print(b[2])             # prints 12
print(b[6])             # prints 0
print(b[[3, 3, 1]])     # prints defaultarray([13, 13, 11]) as expected
print(b[[3, 3, 1, 5]])  # prints 0 instead of defaultarray([13, 13, 11, 0])
And so I'm seriously stuck here, I have no idea even where to look for how index arrays operates in these special methods.

Before you go any further take another look at this problem and see if you can't solve it another way. Does numpy have any kind of sparse array? What about making an array of all 0's that the same size as the one you want and adding both together? I'm not a numpy expert but I think you should try to solve your problem within the numpy API rather than creating a custom extension of numpy that relies on the behavior of their array's private __getitem__ method. You have no guarantee that np.ndarray.__getitem__'s behavior won't change in the next numpy release, breaking your extension.

What is almost certainly happening here is that the argument, index, being passed to your overloaded __getitem__ method is the array [3, 3, 1, 5]. Which is then passed to np.ndarray.__getitem__(self, index). This method would normally look up each of the indexes and return an array but it raises an IndexError because it doesn't know about 5.

I think what you'll have to do here is unpack the array in your overloaded __getitem__ method and feed it to np.ndarray.__getitem__() one value at a time.

fart simpson
Jul 2, 2005

DEATH TO AMERICA
:xickos:

Pie Colony posted:

i'm trying to write haskell-style monads in python. doing something like this is straight-forward:

code:
def with_maybe():
    var('a') <- Maybe(lambda: mdiv(1, 0))
    mreturn(lambda: a)
make a var class with a __lt__ method, make a Maybe class with a __neg__ method, pass some contexts around, etc. but what i'd really like to be able to do is this:

code:
@do(Maybe)
def with_maybe():
    var('a') <- lambda: mdiv(1, 0)
    mreturn(lambda: a)
this requires (temporarily) setting the __neg__ method on FunctionType, which i can't quite figure out how to do. is this possible? (and yes i realize how horrible i am for wanting such a thing)

I don't think you can actually change any methods on the built in FunctionType object, but there's probably some other type of hackery that could get you to a similar place.

Malcolm XML
Aug 8, 2009

I always knew it would end like this.

Pie Colony posted:

i'm trying to write haskell-style monads in python. doing something like this is straight-forward:

code:
def with_maybe():
    var('a') <- Maybe(lambda: mdiv(1, 0))
    mreturn(lambda: a)
make a var class with a __lt__ method, make a Maybe class with a __neg__ method, pass some contexts around, etc. but what i'd really like to be able to do is this:

code:
@do(Maybe)
def with_maybe():
    var('a') <- lambda: mdiv(1, 0)
    mreturn(lambda: a)
this requires (temporarily) setting the __neg__ method on FunctionType, which i can't quite figure out how to do. is this possible? (and yes i realize how horrible i am for wanting such a thing)

Focus on getting the semantics correct over emulating do-notation (use a python macro library for that if you have to)

https://github.com/lihaoyi/macropy is good.

All you have to have is a Functor class which defines fmap, a Monad class which extends it (maybe an applicative too if you want) and defines mreturn and join/bind

Remember that you must follow the Monad laws and functor laws or there's no point (up to strictness and exception and whatever).


Without case classes/algebraic data types it gets kind of awkward though, unfortunately

Adbot
ADBOT LOVES YOU

Pollyanna
Mar 5, 2005

Milk's on them.


Nippashish posted:

tick is a good name for that function.

Instead of having tick update the new board directly I recommend writing it so you use it like this:
code:
def tick(x, y, before):
    ...
    return next state for x,y (True or False)

...

next_world[x,y] = tick(x, y, array_world)
You should also probably have two tick functions, tick_world(before) and tick_position(x, y, before). tick_world is responsible for creating the new world and calling tick_position to fill in each cell and then returning the newly created world.

In general having functions create the values they return instead of accepting an argument that they mutate (like you do in tick now) will make your code a lot easier to reason about since you never need to worry that a function might change some data you give it.

Cool, thanks. I updated it:

Python code:
__author__ = 'rebecca'

# Literal version of the game of life.

import itertools as it
import numpy as np


def tick_world(old, new):
    for a, b in it.product(range(high), range(wide)):
        new[a, b] = tick_position(a, b, old)
    return new


def neighbors(x, y, array):

    cell_neighbors = [
        [x - 1, y - 1], [x, y - 1], [x + 1, y - 1],
        [x - 1, y],                 [x + 1, y],
        [x - 1, y + 1], [x, y + 1], [x + 1, y + 1]
    ]

    return [array[i, j] for i, j in cell_neighbors if 0 <= i < high if 0 <= j < wide]


def tick_position(x, y, before):
    live_neighbors = neighbors(x, y, before).count(True)

    if live_neighbors < 2:
        return False
    elif live_neighbors > 3:
        return False
    elif live_neighbors == 2:
        if before[x, y]:
            return True
    elif live_neighbors == 3:
        return True
    else:
        print "Something went wrong!"

high = int(raw_input("How high?\n>"))
wide = int(raw_input("How wide?\n>"))
round_numbers = int(raw_input("How many rounds?\n>"))

array_world = np.zeros((high, wide), dtype=bool)
next_world = np.zeros((high, wide), dtype=bool)

array_world[2, 2] = True
array_world[3, 2] = True
array_world[1, 2] = True

print "Tick 1:\n"
print array_world

for a in range(round_numbers):

    print "\nTick {0}:\n".format(a+2)

    array_world = tick_world(array_world, next_world)
    next_world = np.zeros((high, wide), dtype=bool)
    print array_world
Now I'm trying to figure out how to make it return an image of the array.

  • Locked thread