суббота, 21 июля 2012 г.

Simple maze solver with python and pyside

I'm practicing in Python, PySide and some algorithms. You can find a simple maze generator/solver below.





from PySide.QtCore import *
from PySide.QtGui import *
import sys
import random
 
class Maze(QWidget):
    def __init__(self, width, height, boxsize=7):
        QWidget.__init__(self)
 
        self.setMazeSize(width, height, boxsize)
        self.generateMazeDFS()
 
    def setMazeSize(self, width, height, boxsize):
        self.maze_width = width
        self.maze_height = height
        self.boxsize = boxsize
        self.setFixedSize(width * boxsize + 10, height * boxsize + 10)
 
    def generateMazeDumb(self, width=None, height=None, sparseness=None):
        if width is None: width = self.maze_width
        if height is None: height = self.maze_height
        if sparseness is None: sparseness = 2
 
        self.vwall = [[random.randint(0,sparseness) for x in range(0, width+1)] for y in range(0, height)]
        self.hwall = [[random.randint(0,sparseness) for x in range(0, width)] for y in range(0, height+1)]
        for x in range(0, width):
            self.hwall[0][x] = 0
            self.hwall[height][x] = 0
        for y in range(0, height):
            self.vwall[y][0] = 0
            self.vwall[y][width] = 0
 
    def generateMazeDFS(self, width=None, height=None, seed_point=None):
        if width is None: width = self.maze_width
        if height is None: height = self.maze_height
        if seed_point is None: seed_point = (random.randint(0, width-1), random.randint(0, height-1))
 
        self.vwall = [[0 for x in range(0, width+1)] for y in range(0, height)]
        self.hwall = [[0 for x in range(0, width)] for y in range(0, height+1)]
 
        maze = [[0 for x in range(0, self.maze_width)] for y in range(0, self.maze_height)]
 
        path = [seed_point]
        while True:
            possible_moves = []
            x, y = path[-1]
 
            if x > 0 and maze[y][x-1] == 0: possible_moves.append('left')
            if x < width-1 and maze[y][x+1] == 0: possible_moves.append('right')
            if y > 0 and maze[y-1][x] == 0: possible_moves.append('up')
            if y < height-1 and maze[y+1][x] == 0: possible_moves.append('down')
 
            if len(possible_moves) > 0:
                next_move = random.choice(possible_moves)
                maze[y][x] = 1
                if next_move == 'left':
                    self.vwall[y][x] = 1
                    path.append((x-1, y))
                    continue
                elif next_move == 'right':
                    self.vwall[y][x+1] = 1
                    path.append((x+1, y))
                    continue
                elif next_move == 'up':
                    self.hwall[y][x] = 1
                    path.append((x, y-1))
                    continue
                elif next_move == 'down':
                    self.hwall[y+1][x] = 1
                    path.append((x, y+1))
                    continue
            else:
                maze[y][x] = -1
                path.pop()
                if len(path) == 0:
                    break
 
    def paintEvent(self, event):
        qp = QPainter()
        qp.begin(self)
        qp.fillRect(0, 0, self.maze_width * self.boxsize + 10, self.maze_height * self.boxsize + 10, Qt.black)
 
        qp.translate(QPoint(5.5, 5.5))
        qp.setPen(Qt.white)
        b = self.boxsize
 
        # vertical walls
        y = 0
        for row in self.vwall:
            x = 0
            for w in row:
                if w == 0: qp.drawLine(x*b, y*b, x*b, (y+1)*b)
                x += 1
            y += 1
 
        # horizontal walls
        y = 0
        for row in self.hwall:
            x = 0
            for w in row:
                if w == 0: qp.drawLine(x*b, y*b, (x+1)*b, y*b)
                x += 1
            y += 1
 
        # path
        prev = None
        for point in self.path:
            x1 = point[0]
            y1 = point[1]
            if prev:
                x2 = prev[0]
                y2 = prev[1]
                qp.fillRect(b*min(x1,x2) + 2, b*min(y1,y2) + 2, b*abs(x1-x2)+b-4, b*abs(y1-y2)+b-4, Qt.green)
            else:
                qp.fillRect(x*b+2, y*b+2, b-4, b-4, Qt.green)
            prev = point
 
        # visited cells
        y = 0
        for row in self.maze:
            x = 0
            for m in row:
                if m == -1: qp.fillRect(x*b+2, y*b+2, b-3, b-3, Qt.gray)
                x += 1
            y += 1
 
        # target
        x, y = self.target
        qp.fillRect(x*b+2, y*b+2, b-3, b-3, Qt.red)
 
        # status
        if self.status:
            qp.setPen(Qt.white)
            qp.setFont(QFont("sans", 72, QFont.Normal))
            qp.drawText(QRect(0, 0, self.maze_width * b, self.maze_height * b),
                Qt.AlignCenter | Qt.AlignVCenter,
                self.status)
 
        qp.end()
 
    def step(self):
        self.step_num += 1
        x, y = self.path[-1]
 
        if (x, y) == self.target:
            self.status = "win"
            return False
 
        if self.step_select == 'random':
            order_list = ['u', 'r', 'd', 'l']
            random.shuffle(order_list)
        else:
            order_list = self.step_select
 
        for order in order_list:
            if order == 'u':
                # try step up
                if y > 0 and self.hwall[y][x] != 0 and self.maze[y-1][x] == 0:
                    self.path.append((x, y-1))
                    self.maze[y-1][x] = self.step_num
                    return True
            elif order == 'r':
                # try step right
                if x < self.maze_width-1 and self.vwall[y][x+1] != 0 and self.maze[y][x+1] == 0:
                    self.path.append((x+1, y))
                    self.maze[y][x+1] = self.step_num
                    return True
            elif order == 'd':
                # try step down
                if y < self.maze_height-1 and self.hwall[y+1][x] != 0 and self.maze[y+1][x] == 0:
                    self.path.append((x, y+1))
                    self.maze[y+1][x] = self.step_num
                    return True
            elif order == 'l':
                # try step left
                if x > 0 and self.vwall[y][x] != 0 and self.maze[y][x-1] == 0:
                    self.path.append((x-1, y))
                    self.maze[y][x-1] = self.step_num
                    return True
 
        # step back
        self.maze[y][x] = -1
        self.path.pop()
 
        if len(self.path) == 0:
            self.status = "loss"
            return False
 
        return True
 
    def init(self):
        self.maze = [[0 for x in range(0, self.maze_width)] for y in range(0, self.maze_height)]
        self.path = [(0, 0)]
        self.step_num = 1
        x, y = self.path[0]
        self.maze[y][x] = 1
        self.target = (self.maze_width-1, self.maze_height-1)
        self.status = False
 
 
class MainWindow(QMainWindow):
    def __init__(self):
        QMainWindow.__init__(self)
        self.setWindowTitle("Maze")
 
        self.maze = Maze(150, 80, 7)
 
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 10, 0)
        hbox.addWidget(self.maze)
        vbox = QVBoxLayout()
        vbox.setContentsMargins(0, 10, 0, 0)
        hbox.addLayout(vbox)
 
        self.le_w = QLineEdit("150")
        vbox.addWidget(QLabel("maze width:"))
        vbox.addWidget(self.le_w)
        self.le_h = QLineEdit("80")
        vbox.addWidget(QLabel("maze height:"))
        vbox.addWidget(self.le_h)
        self.le_b = QLineEdit("7")
        vbox.addWidget(QLabel("cell size:"))
        vbox.addWidget(self.le_b)
 
        self.dm_sparse = QLineEdit("2")
        vbox.addWidget(QLabel("Dumb mode sparseness:"))
        vbox.addWidget(self.dm_sparse)
 
        self.maze_type = QButtonGroup()
        rb_1 = QRadioButton("Dumb random maze")
        rb_1.setChecked(True)
        rb_2 = QRadioButton("DFS maze generator")
        self.maze_type.addButton(rb_1, 0)
        self.maze_type.addButton(rb_2, 1)
        vbox.addWidget(rb_1)
        vbox.addWidget(rb_2)
 
        self.btn_generate = QPushButton("Generate")
        self.btn_generate.clicked.connect(self.generateMaze)
        vbox.addWidget(self.btn_generate)
 
        self.step_order = QLineEdit("random")
        self.step_order.setPlaceholderText("step order")
        vbox.addWidget(self.step_order)
 
        self.btn_solve = QPushButton("Solve")
        self.btn_solve.clicked.connect(self.startSolving)
        vbox.addWidget(self.btn_solve)
 
        self.btn_stop = QPushButton("Stop")
        self.btn_stop.setEnabled(False)
        self.btn_stop.clicked.connect(self.stopSolving)
        vbox.addWidget(self.btn_stop)
 
        vbox.addStretch()
 
        proxy_widget = QWidget()
        proxy_widget.setLayout(hbox)
        self.setCentralWidget(proxy_widget)
 
        self.maze.init()
 
    def doStep(self):
        for k in range(0, 50):
            if not self.maze.step():
                self.timer.stop()
                self.stopSolving()
                break
        self.maze.repaint()
 
    def startSolving(self):
        self.btn_solve.setEnabled(False)
        self.btn_stop.setEnabled(True)
        self.btn_generate.setEnabled(False)
        self.maze.init()
 
        self.maze.step_select = self.step_order.text()
        if self.maze.step_select == '':
            self.maze.step_select = 'random'
 
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.doStep)
        self.timer.setInterval(50)
        self.timer.start()
 
    def stopSolving(self):
        self.btn_stop.setEnabled(False)
        self.btn_solve.setEnabled(True)
        self.btn_generate.setEnabled(True)
        self.timer.stop()
 
    def generateMaze(self):
        w = max(2, int(self.le_w.text()))
        h = max(2, int(self.le_h.text()))
        b = max(5, int(self.le_b.text()))
        sparseness = max(0, int(self.dm_sparse.text()))
 
        self.maze.setMazeSize(w, h, b)
        self.maze.init()
 
        if self.maze_type.checkedId() == 0:
            self.maze.generateMazeDumb(sparseness=sparseness)
        elif self.maze_type.checkedId() == 1:
            self.maze.generateMazeDFS()
        self.maze.repaint()
 
 
app = QApplication(sys.argv)
 
mw = MainWindow()
mw.show()
 
sys.exit(app.exec_())
 


пятница, 20 июля 2012 г.

CX_Find_Cell_With_Point and 3ddp (Fluent)

There is a function CX_Find_Cell_With_Point. It's not listed in official documentation but it works well when you need to find a cell which contains specified point. Otherwise one must use very slow dumb search or develop  some sofisticated search algorithms. But there is a glitch with its use. As it's undocumented, there is no guarantee that it even compiles.
`CX_Find_Cell_With_Point' does not work with double precision (3ddp) Fluent solver!
That's true for Fluent 6.2.16 on lnamd64 (at least). With 3ddp it always returns NULL.

And if you're here for example, here is a code:

#include "udf.h"
#include "surf.h"
#include "para.h"
 
void some_function()
{
    cell_t c;
    Thread *t;
    CX_Cell_Id cx_cell;
    CX_Cell_Id *cx_cell_ptr;
    real pt[3];
 
    CX_Start_ND_Point_Search(); /* call this before actual search */
                                /* perhaps it'll do some initializations */
    for (k = 0; k < 1000; k ++) {
        /* fill pt[3] with data */
        pt[0] = 0.0; pt[1] = 0.0; pt[2] = k / 1000.0;
 
        /* perform search */
        cx_cell_ptr = CX_Find_Cell_With_Point(pt);
        if (cx_cell_ptr) {   /* function can return NULL if no cell was found */
            cx_cell = *cx_cell_ptr;
            c = RP_CELL(&cx_cell);  /* get cell number */
            t = RP_THREAD(&cx_cell);  /* get cell thread */
 
            /* ... do your calculations ... */
 
        } else {
            /* no cell was found */
        }
 
    }
 
    /* It's a good practice to clean up */
    CX_End_ND_Point_Search();
}
 

пятница, 13 июля 2012 г.

another fluent 3ddp parallel benchmark

Another fluent 3ddp benchmark on Phenom II X6 1090T.
Unfortunatelly I don't remember what case was used.

procs; time per iteration; wall time; cpu time

smpi:
1 4.577 457.661 457.330
2 2.488 248.802 496.820
3 1.792 179.155 536.430
4 1.475 147.455 587.010
5 1.221 122.108 605.510
6 1.081 108.132 632.970
7 2.314 231.399 1202.540

nmpi:
5 1.569 156.866 669.320 nmpi
6 1.253 125.323 648.960 nmpi
7 3.235 323.487 1728.310 nmpi