So I've been programming a snake game using python mode for processing, but I have been having an issue with the list I have set up for keeping track of the body of the snake itself.
My current implementation uses a list of vectors to keep track of the location of each segment of the snake. I then loop through this list to display the squares for each segment. At the beginning of the game, the list only has 1 entry (the head), but upon eating a piece of food, I insert a new vector to the front of the list with the same value as the current head. I then update the list but looping through it and finally, I update the head by using a velocity vector.
scl = 10
dim = 20
def setup():
global s
global f
size(dim * scl, dim * scl)
s = Snake()
f = Food()
def draw():
background(201)
global s
global f
if s.eat(f):
f.location()
s.update()
s.display()
f.display()
delay(50)
class Snake:
def __init__(self):
self.body = [PVector(0, 0)]
self.v = PVector(1, 0)
self.total = 1
def update(self):
for i in range(self.total - 1):
self.body[self.total - 1 - i] = self.body[self.total - 2 - i]
print("Position")
print(self.body)
self.body[0].x += self.v.x * scl
print(self.body)
self.body[0].y += self.v.y * scl
print(self.body)
def display(self):
fill(101)
for i in range(self.total):
rect(self.body[i].x + 1, self.body[i].y + 1, scl - 2, scl - 2)
def eat(self, p):
tmp = self.body[:]
dis = dist(self.body[0].x, self.body[0].y, p.pos.x, p.pos.y)
if dis < 1:
self.total += 1
self.body.insert(0, tmp[0])
return True
else:
return False
I expect the output to be a list of differing vectors, each that draw a square next to the previous and next entries. Instead, after eating food, all the vectors are the same within the body list. Does anyone know how I can fix this?
You seem to misunderstood, how python's list handles it's values.
tmp = self.body[:]
makes shallow copy, not deep copy. And:
self.body[...] = self.body[...]
doesn't copy the value. It just passes the value from one place, to another. So when you move your values in self.body by one offset, the first and the second element will end pointing to the same value.
Try something like this:
def update(self):
for i in range(self.total - 1):
self.body[self.total - 1 - i] = self.body[self.total - 2 - i]
print("Position")
print(self.body)
self.body[0] = PVector(self.body[0].x + self.v.x * scl, self.body[0].y + self.v.y * scl)
print(self.body)
and in eat function:
self.body.insert(0, PVector(tmp[0].x, tmp[0].y))
Related
I made an attempt to solve Uncle Bobs bowling game kata (http://www.butunclebob.com/ArticleS.UncleBob.TheBowlingGameKata) but didn't really find a solution that felt pythonic enough.
This solution is more or less an adaptation of Martins C++ solution and uses array indexes to calculate scores for strikes and spares. It works but doesn't feel quite as pythonic as I would like it to be.
class Game():
def __init__(self):
self.rolls = []
def roll(self, pins):
self.rolls.append(pins)
def score_c(self):
total_score = 0
frame_index = 0
for frame in range(10):
if self.rolls[frame_index] == 10:
total_score += 10 + self.rolls[frame_index + 1] + self.rolls[frame_index + 2]
frame_index +=1
elif self.rolls[frame_index] + self.rolls[frame_index + 1] == 10:
total_score += 10 + self.rolls[frame_index + 1]
frame_index += 2
else:
total_score += self.rolls[frame_index] + self.rolls[frame_index + 1]
frame_index += 2
return total_score
I could have used convenience functions for strike and spare conditions, but you get the picture.
But I thought there must be a way to do it without accessing the rolls array directly though indexes. That feels like a very c-like way of doing it and incrementing frame_index directly definitely doesn't feel right in python. So I think there must be a neater way to do it. I made an attempt below which didn't really work for perfect games.
This one use a generator to provide frames which felt pretty neat but it also meant that 0 had to be added for strikes to make complete 2 roll frames.
class Game():
def __init__(self):
self.rolls = []
def _frame_iterator(self):
for i in range(0, 20, 2):
yield (self.rolls[i], self.rolls[i+1])
def roll(self, pins):
self.rolls.append(pins)
if pins == 10:
self.rolls.append(0)
def score(self):
total_score = 0
spare = False
strike = False
for frame in self._frame_iterator():
if spare:
total_score += frame[0]
spare = False
if strike:
total_score += frame[1]
strike = False
if frame[0] + frame[1] == 10:
spare = True
if frame[0] == 10:
strike = True
total_score += frame[0] + frame[1]
return total_score
My questions are basically, has anyone solved the bowling kata in Python in a different and more pythonic way than uncle bobs C++ solution? And suggestions how to improve on my attempt?
This is definitely a different approach (implementing most of the rules in roll(), instead of score()), and I think it's pretty pythonic too.
class Game(object):
def __init__(self):
self._score = [[]]
def roll(self, pins):
# start new frame if needed
if len(self._score[-1]) > 1 or 10 in self._score[-1]:
self._score.append([])
# add bonus points to the previous frames
for frame in self._score[-3:-1]:
if sum(frame[:2]) >= 10 and len(frame) < 3:
frame.append(pins)
# add normal points to current frame
for frame in self._score[-1:10]:
frame.append(pins)
def score(self):
return sum(sum(x) for x in self._score)
The main idea here is instead of storing all rolls in a single list, to make a list of frames that contains a list of rolls (and bonus points) for each frame.
What makes it pythonic is for example the generator expression in the score method.
Another pythonic example is the use of list slices. A previous version of middle part of roll() I did looked like:
for i in [2, 3]:
if len(self._score) >= i:
sum(self._score[-i][:2]) >= 10 and len(self._score[-i]) < 3:
self._score[-i].append(pins)
The elegant thing of the current version using a list slice is that you don't have to check whether the list is long enough to look 1 or 2 frames back. Moreover, you get a nice local variable (frame = self._score[-i]) for free, without having to dedicate a separate line for it.
I'm working on a 2-player board game (e.g. connect 4), with parametric board size h, w. I want to check for winning condition using hw-sized bitboards.
In game like chess, where board size is fixed, bitboards are usually represented with some sort of 64-bit integer. When h and w are not constant and maybe very big (let's suppose 30*30) are bitboards a good idea? If so, are the any data types in C/C++ to deal with big bitboards keeping their performances?
Since I'm currently working on python a solution in this language is appreciated too! :)
Thanks in advance
I wrote this code while ago just to play around with the game concept. There is no intelligence behaviour involve. just random moves to demonstrate the game. I guess this is not important for you since you are only looking for a fast check of winning conditions. This implementation is fast since I did my best to avoid for loops and use only built-in python/numpy functions (with some tricks).
import numpy as np
row_size = 6
col_size = 7
symbols = {1:'A', -1:'B', 0:' '}
def was_winning_move(S, P, current_row_idx,current_col_idx):
#****** Column Win ******
current_col = S[:,current_col_idx]
P_idx= np.where(current_col== P)[0]
#if the difference between indexes are one, that means they are consecutive.
#we need at least 4 consecutive index. So 3 Ture value
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** Column Win ******
current_row = S[current_row_idx,:]
P_idx= np.where(current_row== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** Diag Win ******
offeset_from_diag = current_col_idx - current_row_idx
current_diag = S.diagonal(offeset_from_diag)
P_idx= np.where(current_diag== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
#****** off-Diag Win ******
#here 1) reverse rows, 2)find new index, 3)find offest and proceed as diag
reversed_rows = S[::-1,:] #1
new_row_idx = row_size - 1 - current_row_idx #2
offeset_from_diag = current_col_idx - new_row_idx #3
current_off_diag = reversed_rows.diagonal(offeset_from_diag)
P_idx= np.where(current_off_diag== P)[0]
is_idx_consecutive = sum(np.diff(P_idx)==1)>=3
if is_idx_consecutive:
return True
return False
def move_at_random(S,P):
selected_col_idx = np.random.permutation(range(col_size))[0]
#print selected_col_idx
#we should fill in matrix from bottom to top. So find the last filled row in col and fill the upper row
last_filled_row = np.where(S[:,selected_col_idx] != 0)[0]
#it is possible that there is no filled array. like the begining of the game
#in this case we start with last row e.g row : -1
if last_filled_row.size != 0:
current_row_idx = last_filled_row[0] - 1
else:
current_row_idx = -1
#print 'col[{0}], row[{1}]'.format(selected_col,current_row)
S[current_row_idx, selected_col_idx] = P
return (S,current_row_idx,selected_col_idx)
def move_still_possible(S):
return not (S[S==0].size == 0)
def print_game_state(S):
B = np.copy(S).astype(object)
for n in [-1, 0, 1]:
B[B==n] = symbols[n]
print B
def play_game():
#initiate game state
game_state = np.zeros((6,7),dtype=int)
player = 1
mvcntr = 1
no_winner_yet = True
while no_winner_yet and move_still_possible(game_state):
#get player symbol
name = symbols[player]
game_state, current_row, current_col = move_at_random(game_state, player)
#print '******',player,(current_row, current_col)
#print current game state
print_game_state(game_state)
#check if the move was a winning move
if was_winning_move(game_state,player,current_row, current_col):
print 'player %s wins after %d moves' % (name, mvcntr)
no_winner_yet = False
# switch player and increase move counter
player *= -1
mvcntr += 1
if no_winner_yet:
print 'game ended in a draw'
player = 0
return game_state,player,mvcntr
if __name__ == '__main__':
S, P, mvcntr = play_game()
let me know if you have any question
UPDATE: Explanation:
At each move, look at column, row, diagonal and secondary diagonal that goes through the current cell and find consecutive cells with the current symbol. avoid scanning the whole board.
extracting cells in each direction:
column:
current_col = S[:,current_col_idx]
row:
current_row = S[current_row_idx,:]
Diagonal:
Find the offset of the desired diagonal from the
main diagonal:
diag_offset = current_col_idx - current_row_idx
current_diag = S.diagonal(offset)
off-diagonal:
Reverse the rows of matrix:
S_reversed_rows = S[::-1,:]
Find the row index in the new matrix
new_row_idx = row_size - 1 - current_row_idx
current_offdiag = S.diagonal(offset)
I have been trying to solve this for 2 days straight and I just can't find a valid algorithm. Given a Chess board, and a number of pieces, I have to check if said board can be toured by the pieces, with the condition that each piece can only visit an square once. I know it's some kind of multiple backtracking, but I can't get it to work. (I only have been able to implement a general knight's tour for individual pieces)
tablero is a class of the board, that holds a name, a list of pieces, a list with prohibited positions, a list with the free positions, and a tuple with the dimensions of the board.
ficha is the class of a piece, it holds a name (nombre), a tuple with its position (posicion), a list with its valid movements (movimientos) (For example, a pawn's list would be [ [0,1] ], meaning it can only move forward 1)
Any insight is welcome.
Here are the classes (Feel free to add/remove any method).
def legal(pos,dimensiones):
if pos[0] >= 0 and pos[0] < dimensiones[0] and pos[1] >= 0 and pos[1] < dimensiones[0]:
return True
else:
return False
class board:
def __init__(self,name,pieces,dimention,prohibited_positions):
self.name = name
self.pieces = pieces
self.dimention = dimention
self.prohibited_positions = prohibited_positions
self.free_positions = []
for x in range(dimention[0]):
for y in range(dimention[1]):
self.free_positions.append([x,y])
for x,y in self.prohibited_positions:
if [x,y] in self.free_positions:
self.free_positions.remove([x,y])
for piece in self.pieces:
if self.piece.position in self.free_positions:
self.free_positions.remove(piece.position)
def append(self,piece):
pos = piece.position
if pos in self.free_positions:
self.pieces.append(piece)
self.free_positions.remove(pos)
class piece:
def __init__(self,name,position,move_offsets):
self.name=name
self.position=position
self.move_offsets=move_offsets
self.possible_movements=move_offsets
def setPos(self,pos):
self.position=pos
def ValidMovements(self,dim,free_positions,prohibited_positions):
aux = []
for i in self.possible_movements:
newX = self.position[0] + i[0]
newY = self.position[1] + i[1]
newPos = [newX,newY]
if legal(newPos,dim):
aux.append(newPos)
for i in list(aux):
if i not in free_positions:
aux.remove(i)
I have been trying to write my own version of Conway's Game of Life as practice for Python using Pygame. I first wrote the functions for initializing the game field, and then calculating the next generation. I verified it's functionality using the console to print the results and verify that they returned the expected results (just 2 generations deep by hand on a 5x5 grid).
An important note of how I am calculating the neighbors... Instead of doing a for loop through the entire array and doing for loops to count for each cell, I have implemented an array that holds the neighbor counts. Only making changes when a cells status is changed. This means I don't waste time calculating neighbors for cells that have not changed.
When it came time to use Pygame to display the array with rectangles, I wrote the following program. At first I was drawing the screen by filling in the entire screen white, and then drawing the live cells as black (this can be done by commenting out the else statement in update()). I expected this to work as normal, but when I ran the program all I ended up with was the screen filling in black.
I was perplexed by the result so i drew white rectangles for the unpopulated cells (using the else statement. And got a better looking result, but instead of the cells eventually all dying, they eventually multiplied across the whole screen. This is opposite of what I expected, as I was expecting it to eventually stabilize.
Anyone know what I am doing wrong? I know that this is not the best way of writing this program, I welcome comments of how I can make it better.
RETURN = run simulation
'R' = randomize
'T' = tick one generation
'C' = clear game field
'N' = display neighbor map
import pygame
from pygame.locals import *
import numpy as np
from random import *
import copy
fieldSize = [100,50]
cellSize = 10 # size of >10 is recommended to see neighbor count
windowSize = [fieldSize[0]*cellSize, fieldSize[1]*cellSize]
# calculate the last cell in each axis so it is not done repeatedly
lastCell = [(fieldSize[0]-1), (fieldSize[1]-1)]
dX = float(windowSize[0])/float(fieldSize[0])
dY = float(windowSize[1])/float(fieldSize[1])
colorAlive = [0,125,0]
colorDead = [0, 0, 0]
# todo list
# 1. make cLife take in the field size
# 2. convert random functions to numpy.random.randint
class cLife():
def randomize(self):
self.neighbors = np.zeros(fieldSize)
# fill in the game field with random numbers
for x in range(fieldSize[0]):
for y in range(fieldSize[1]):
if(randint(0,99)<20):
self.gameField[x][y] = 1
self.updateNeighbors([x,y], True)
else:
self.gameField[x][y] = 0
def displayNeighbors(self, surface):
self.drawField(surface)
for x in range(fieldSize[0]):
for y in range(fieldSize[1]):
neighborCount=font.render(str(int(self.neighbors[x][y])), 1,(200,200,200))
surface.blit(neighborCount, (x*dX+dX/3, y*dY+dY/3.5))
pygame.display.flip()
# This is the function to update the neighbor map, the game field is torroidal so the complexity is greatly
# increased. I have handcoded each instruction to avoid countless if statements and for loops.
# Hopefully, this has drastically improved the performance. Using this method also allows me to avoid calculating
# the neighbor map for every single cell because the neighbor map is updated only for the cells affected by a change.
def updateNeighbors(self, pos, status):
if(status == True):
change = 1
else:
change = -1
# testing for the cells in the center of the field (most cells are in the center so this is first)
# cells are filled in starting on the top-left corner going clockwise
if((pos[0]>0 and pos[0]<lastCell[0])and(pos[1]>0 and pos[1]<lastCell[1])):
self.neighbors[pos[0]-1][pos[1]-1] += change
self.neighbors[pos[0]][pos[1]-1] += change
self.neighbors[pos[0]+1][pos[1]-1] += change
self.neighbors[pos[0]+1][pos[1]] += change
self.neighbors[pos[0]+1][pos[1]+1] += change
self.neighbors[pos[0]][pos[1]+1] += change
self.neighbors[pos[0]-1][pos[1]+1] += change
self.neighbors[pos[0]-1][pos[1]] += change
elif(pos[0] == 0): # left edge
if(pos[1] == 0): # top left corner
self.neighbors[lastCell[0]][lastCell[1]] += change
self.neighbors[0][lastCell[1]] += change
self.neighbors[1][lastCell[1]] += change
self.neighbors[1][0] += change
self.neighbors[1][1] += change
self.neighbors[0][1] += change
self.neighbors[lastCell[0]][1] += change
self.neighbors[lastCell[0]][0] += change
elif(pos[1] == lastCell[1]): # bottom left corner
self.neighbors[lastCell[0]][pos[1]-1] += change
self.neighbors[0][pos[1]-1] += change
self.neighbors[1][pos[1]-1] += change
self.neighbors[1][pos[1]] += change
self.neighbors[1][0] += change
self.neighbors[0][0] += change
self.neighbors[lastCell[0]][0] += change
self.neighbors[lastCell[0]][pos[1]] += change
else: # everything else
self.neighbors[lastCell[0]][pos[1]-1] += change
self.neighbors[0][pos[1]-1] += change
self.neighbors[1][pos[1]-1] += change
self.neighbors[1][pos[1]] += change
self.neighbors[1][pos[1]+1] += change
self.neighbors[0][pos[1]+1] += change
self.neighbors[lastCell[0]][pos[1]+1] += change
self.neighbors[lastCell[0]][pos[1]] += change
elif(pos[0] == lastCell[0]): # right edge
if(pos[1] == 0): # top right corner
self.neighbors[pos[0]-1][lastCell[1]] += change
self.neighbors[pos[0]][lastCell[1]] += change
self.neighbors[0][lastCell[1]] += change
self.neighbors[0][0] += change
self.neighbors[0][1] += change
self.neighbors[pos[0]][1] += change
self.neighbors[pos[0]-1][1] += change
self.neighbors[pos[0]-1][0] += change
elif(pos[1] == lastCell[1]): # bottom right corner
self.neighbors[pos[0]-1][pos[1]-1] += change
self.neighbors[pos[0]][pos[1]-1] += change
self.neighbors[0][pos[1]-1] += change
self.neighbors[0][pos[1]] += change
self.neighbors[0][0] += change
self.neighbors[pos[0]][0] += change
self.neighbors[pos[0]-1][0] += change
self.neighbors[pos[0]-1][pos[1]] += change
else: # everything else
self.neighbors[pos[0]-1][pos[1]-1] += change
self.neighbors[pos[0]][pos[1]-1] += change
self.neighbors[0][pos[1]-1] += change
self.neighbors[0][pos[1]] += change
self.neighbors[0][pos[1]+1] += change
self.neighbors[pos[0]][pos[1]+1] += change
self.neighbors[pos[0]-1][pos[1]+1] += change
self.neighbors[pos[0]-1][pos[1]] += change
elif(pos[1] == 0): # top edge, corners already taken care of
self.neighbors[pos[0]-1][lastCell[1]] += change
self.neighbors[pos[0]][lastCell[1]] += change
self.neighbors[pos[0]+1][lastCell[1]] += change
self.neighbors[pos[0]+1][0] += change
self.neighbors[pos[0]+1][1] += change
self.neighbors[pos[0]][1] += change
self.neighbors[pos[0]-1][1] += change
self.neighbors[pos[0]-1][0] += change
elif(pos[1] == lastCell[1]): # bottom edge, corners already taken care of
self.neighbors[pos[0]-1][pos[1]-1] += change
self.neighbors[pos[0]][pos[1]-1] += change
self.neighbors[pos[0]+1][pos[1]-1] += change
self.neighbors[pos[0]+1][pos[1]] += change
self.neighbors[pos[0]+1][0] += change
self.neighbors[pos[0]][0] += change
self.neighbors[pos[0]-1][0] += change
self.neighbors[pos[0]-1][pos[1]] += change
def nextGeneration(self):
# copy the neighbor map, because changes will be made during the update
self.neighborsOld = copy.deepcopy(self.neighbors)
for x in range(fieldSize[0]):
for y in range(fieldSize[1]):
# Any live cell with fewer than two live neighbours dies, as if caused by under-population.
if(self.gameField[x][y] == 1 and self.neighborsOld[x][y] < 2):
self.gameField[x][y] = 0
self.updateNeighbors([x,y], False)
# Any live cell with more than three live neighbours dies, as if by overcrowding.
elif(self.gameField[x][y] == 1 and self.neighborsOld[x][y] >3):
self.gameField[x][y] = 0
self.updateNeighbors([x,y], False)
# Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
elif(self.gameField[x][y] == 0 and self.neighborsOld[x][y] == 3):
self.gameField[x][y] = 1
self.updateNeighbors([x,y], True)
def drawField(self, surface):
surface.fill(colorDead)
# loop through and draw each live cell
for x in range(fieldSize[0]):
for y in range(fieldSize[1]):
if(self.gameField[x][y] == 1):
pygame.draw.rect(surface, colorAlive, [dX*x, dY*y, dX, dY])
pygame.display.flip()
def __init__(self):
# initialize the game field and neighbor map with zeros
self.gameField = np.zeros(fieldSize)
self.neighbors = np.zeros(fieldSize)
# begining of the program
game = cLife()
pygame.init()
surface = pygame.display.set_mode(windowSize)
pygame.display.set_caption("Conway\'s Game of Life")
clock = pygame.time.Clock()
pygame.font.init()
font=pygame.font.Font(None,10)
surface.fill(colorDead)
game.randomize()
game.drawField(surface)
pygame.display.flip()
running = False
while True:
#clock.tick(60)
# handling events
for event in pygame.event.get():
if(event.type == pygame.MOUSEBUTTONDOWN):
mousePos = pygame.mouse.get_pos()
x = int(mousePos[0]/dX)
y = int(mousePos[1]/dY)
if(game.gameField[x][y] == 0):
game.gameField[x][y] = 1
game.updateNeighbors([x, y], True)
game.drawField(surface)
else:
game.gameField[x][y] = 0
game.updateNeighbors([x, y], False)
game.drawField(surface)
elif(event.type == pygame.QUIT):
pygame.quit()
elif(event.type == pygame.KEYDOWN):
# return key starts and stops the simulation
if(event.key == pygame.K_RETURN):
if(running == False):
running = True
else:
running = False
# 't' key ticks the simulation forward one generation
elif(event.key == pygame.K_t and running == False):
game.nextGeneration()
game.drawField(surface)
# 'r' randomizes the playfield
elif(event.key == pygame.K_r):
game.randomize()
game.drawField(surface)
# 'c' clears the game field
elif(event.key == pygame.K_c):
running = False
game.gameField = np.zeros(fieldSize)
game.neighbors = np.zeros(fieldSize)
game.drawField(surface)
# 'n' displays the neighbor map
elif(event.key == pygame.K_n):
game.displayNeighbors(surface)
if(running == True):
game.nextGeneration()
game.drawField(surface)
self.neighborsOld = self.neighbors does not copy the map, it only points to it.
See :
a = [[1,2],[3,4]]
b = a
b[0][0] = 9
>>> a
[[9, 2], [3, 4]]
You need to either make a copy (a[:]) for every row in a, or use the copy module and use deepcopy:
b = [x[:] for x in a]
or
import copy
b = copy.deepcopy(a)
Either way, it results in
b[0][0] = 9
>>> a
[[1, 2], [3, 4]]
I've written some python code to calculate a certain quantity from a cosmological simulation. It does this by checking whether a particle in contained within a box of size 8,000^3, starting at the origin and advancing the box when all particles contained within it are found. As I am counting ~2 million particles altogether, and the total size of the simulation volume is 150,000^3, this is taking a long time.
I'll post my code below, does anybody have any suggestions on how to improve it?
Thanks in advance.
from __future__ import division
import numpy as np
def check_range(pos, i, j, k):
a = 0
if i <= pos[2] < i+8000:
if j <= pos[3] < j+8000:
if k <= pos[4] < k+8000:
a = 1
return a
def sigma8(data):
N = []
to_do = data
print 'Counting number of particles per cell...'
for k in range(0,150001,8000):
for j in range(0,150001,8000):
for i in range(0,150001,8000):
temp = []
n = []
for count in range(len(to_do)):
n.append(check_range(to_do[count],i,j,k))
to_do[count][1] = n[count]
if to_do[count][1] == 0:
temp.append(to_do[count])
#Only particles that have not been found are
# searched for again
to_do = temp
N.append(sum(n))
print 'Next row'
print 'Next slice, %i still to find' % len(to_do)
print 'Calculating sigma8...'
if not sum(N) == len(data):
return 'Error!\nN measured = {0}, total N = {1}'.format(sum(N), len(data))
else:
return 'sigma8 = %.4f, variance = %.4f, mean = %.4f' % (np.sqrt(sum((N-np.mean(N))**2)/len(N))/np.mean(N), np.var(N),np.mean(N))
I'll try to post some code, but my general idea is the following: create a Particle class that knows about the box that it lives in, which is calculated in the __init__. Each box should have a unique name, which might be the coordinate of the bottom left corner (or whatever you use to locate your boxes).
Get a new instance of the Particle class for each particle, then use a Counter (from the collections module).
Particle class looks something like:
# static consts - outside so that every instance of Particle doesn't take them along
# for the ride...
MAX_X = 150,000
X_STEP = 8000
# etc.
class Particle(object):
def __init__(self, data):
self.x = data[xvalue]
self.y = data[yvalue]
self.z = data[zvalue]
self.compute_box_label()
def compute_box_label(self):
import math
x_label = math.floor(self.x / X_STEP)
y_label = math.floor(self.y / Y_STEP)
z_label = math.floor(self.z / Z_STEP)
self.box_label = str(x_label) + '-' + str(y_label) + '-' + str(z_label)
Anyway, I imagine your sigma8 function might look like:
def sigma8(data):
import collections as col
particles = [Particle(x) for x in data]
boxes = col.Counter([x.box_label for x in particles])
counts = boxes.most_common()
#some other stuff
counts will be a list of tuples which map a box label to the number of particles in that box. (Here we're treating particles as indistinguishable.)
Using list comprehensions is much faster than using loops---I think the reason is that you're basically relying more on the underlying C, but I'm not the person to ask. Counter is (supposedly) highly-optimized as well.
Note: None of this code has been tested, so you shouldn't try the cut-and-paste-and-hope-it-works method here.