-
Notifications
You must be signed in to change notification settings - Fork 32
rewrite Pegsol in python #20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,4 @@ | ||
| test: | ||
| ruby converter.rb | ||
| # ruby -I. generator.rb | ||
| ./pegsol-instances.py # Only analyzes instances, doesn't generate them. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a simple test for the new Python script? |
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,33 @@ | ||||||
| Adaption of Puzzle library from Solipeg 2.2 | ||||||
| (http://ourworld.compuserve.com/homepages/cade/psionsof.htm, changed all | ||||||
| unmovable pegs to movable pegs) | ||||||
|
|
||||||
| Solipeg, a Classic Marble Puzzle Game for the Psion Series 3a, 3c, Siena | ||||||
| Version 2.2 (and 2.2 Lite) Copyright (C) 1993, 1994, 1995, 1996 J Cade Roux | ||||||
|
|
||||||
| "The game Puzzle-Peg (Third Edition, 1924, Lubbers & Bell Mfg. Co.,Clinton, | ||||||
| Iowa, USA, 50 cents) included a small booklet (23 pages),"Problems in | ||||||
| Puzzle-Peg", which details over 100 different end-game-type problems which | ||||||
| can be played on the standard English board, sent in by players of their | ||||||
| earlier versions. It also contained adverts for other games of theirs. All | ||||||
| 104 of the problems are included in this distribution - embedded in the | ||||||
| library. The Lite version contains only the standard two starting positions, | ||||||
| although an external library is provided which is comparable to the internal | ||||||
| library." | ||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
| # pddl-generators memo | ||||||
|
|
||||||
| The original code was probably written for ruby 1.8. | ||||||
| The code was fixed for ruby 2.x. | ||||||
| This ruby code reproduces instances takes from the book. | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
|
||||||
| `ruby converter.rb` seems to generate a template for `instances.rb`. | ||||||
| `ruby -I. generator.rb` will generate problem files. | ||||||
|
|
||||||
|
|
||||||
| A new randomized implementation (generate.py) will generate a new board | ||||||
| which is not guaranteed to be solvable. | ||||||
| It generates a random board state and has a customizable board size and a corner drop off size. | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,142 @@ | ||
| #!/bin/env python3 | ||
|
|
||
| """ | ||
| A randomized Pegsol generator for sequential STRIPS with negative preconditions. | ||
| """ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add: "Generated instances are not guaranteed to be solvable." |
||
|
|
||
|
|
||
| import argparse | ||
| import random | ||
| import numpy as np | ||
| import numpy.random as nr | ||
| import functools | ||
| import textwrap | ||
|
|
||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("--rows", type=int, default=7, help="Number of rows, must be odd.") | ||
| parser.add_argument("--cols", type=int, default=7, help="Number of columns, must be odd.") | ||
| parser.add_argument("--rowcuts", type=int, default=2, help="Number of rows to cut from each corner. Should be less than floor(rows/2)") | ||
| parser.add_argument("--colcuts", type=int, default=2, help="Number of cols to cut from each corner. Should be less than floor(cols/2)") | ||
| parser.add_argument("occupancy", type=float, help="Probability that the pegs are initially occupied. (0, 1]") | ||
| # parser.add_argument("--european", action="store_true", help="If present, corners are cut in a triangular form (a European pegsol board)") | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment. |
||
| parser.add_argument("--seed", type=int, default=1) | ||
| args = parser.parse_args() | ||
|
|
||
|
|
||
| random.seed(args.seed) | ||
|
|
||
| # for development | ||
| # rows = 7 | ||
| # cols = 7 | ||
| # rowcuts = 2 | ||
| # colcuts = 2 | ||
| # occupancy = 0.8 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove comment? |
||
|
|
||
|
|
||
| rows = args.rows | ||
| cols = args.cols | ||
| rowcuts = args.rowcuts | ||
| colcuts = args.colcuts | ||
| occupancy = args.occupancy | ||
|
|
||
| assert rows % 2 == 1, f"rows={rows} must be odd" | ||
| assert cols % 2 == 1, f"cols={cols} must be odd" | ||
| assert rowcuts < rows // 2, f"rowcuts={rowcuts} must be less than floor(rows={rows}/2)" | ||
| assert rowcuts < rows // 2, f"rowcuts={rowcuts} must be less than floor(rows={rows}/2)" | ||
| assert colcuts < cols // 2, f"colcuts={colcuts} must be less than floor(cols={cols}/2)" | ||
| assert occupancy <= 1.0, f"occupancy={occupancy} must be in 0 < occupancy <= 1" | ||
| assert 0.0 < occupancy, f"occupancy={occupancy} must be in 0 < occupancy <= 1" | ||
|
|
||
| total = rows * cols - 4 * rowcuts * colcuts | ||
|
|
||
| ################################################################ | ||
| # board generation | ||
|
|
||
| INVALID = -1 | ||
| FREE = 0 | ||
| PEG = 1 | ||
|
|
||
| # initialize free:0 or peg:1 | ||
| board = (nr.random((rows, cols)) < occupancy).astype(int) | ||
|
|
||
| # cut the corners | ||
| board[:rowcuts,:colcuts] = -1 | ||
| board[-rowcuts:,:colcuts] = -1 | ||
| board[:rowcuts,-colcuts:] = -1 | ||
| board[-rowcuts:,-colcuts:] = -1 | ||
|
|
||
|
|
||
| ################################################################ | ||
| # pddl generation | ||
|
|
||
| def pos(i,j): | ||
| return f"pos-{i}-{j}" | ||
|
|
||
|
|
||
| def objects(): | ||
| return [ pos(i,j) for i,j in zip(*np.where(board != INVALID)) ] + ["-", "location"] | ||
|
|
||
|
|
||
| def init(): | ||
| return [ | ||
| ["=", ["total-cost"], "0"], | ||
| ["move-ended"], | ||
| *list(in_line()), | ||
| *[ ["free", pos(i,j)] for i,j in zip(*np.where(board == FREE)) ], | ||
| *[ ["occupied", pos(i,j)] for i,j in zip(*np.where(board == PEG)) ], | ||
| ] | ||
|
|
||
|
|
||
| def in_line(): | ||
| for i,j in zip(*np.where(board != INVALID)): | ||
| # horizontal move | ||
| if j+2 < cols and board[i, j] != INVALID and board[i, j+1] != INVALID and board[i, j+2] != INVALID: | ||
| yield ["IN-LINE", pos(i,j), pos(i,j+1), pos(i,j+2), ] | ||
| yield ["IN-LINE", pos(i,j+2), pos(i,j+1), pos(i,j), ] | ||
| # vertical move | ||
| if i+2 < rows and board[i, j] != INVALID and board[i+1, j] != INVALID and board[i+2, j] != INVALID: | ||
| yield ["IN-LINE", pos(i,j), pos(i+1,j), pos(i+2,j), ] | ||
| yield ["IN-LINE", pos(i+2,j), pos(i+1,j), pos(i,j), ] | ||
|
|
||
|
|
||
| def goal(): | ||
| return [ | ||
| "and", | ||
| *[ ["free", pos(i,j)] for i,j in zip(*np.where(board != INVALID)) if i != (rows//2) and j != (cols//2) ], | ||
| ["occupied", pos((rows//2), (cols//2))] | ||
| ] | ||
|
|
||
|
|
||
| def problem(): | ||
| return [ | ||
| "define", ["problem", "pegsolprob"], | ||
| [":domain", "pegsolitaire-sequential"], | ||
| [":objects", *objects()], | ||
| [":init", *init()], | ||
| [":goal", goal()], | ||
| [":metric", "minimize", ["total-cost"]], | ||
| ] | ||
|
|
||
|
|
||
| def print_sexp(obj,indent=0): | ||
| if isinstance(obj, str): | ||
| return obj | ||
| assert isinstance(obj, list), f"{obj} is not str or list" | ||
| if len(obj) > 4: | ||
| return textwrap.indent( | ||
| "(" + "\n".join( | ||
| map(lambda x: print_sexp(x,indent+1), | ||
| obj)) + ")", | ||
| " "*indent) | ||
| else: | ||
| return textwrap.indent( | ||
| "(" + " ".join( | ||
| map(lambda x: print_sexp(x,indent+1), | ||
| obj)) + ")", | ||
| " "*indent) | ||
|
|
||
| print_sexp(problem()) | ||
|
|
||
|
|
||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| #!/usr/bin/ruby | ||
|
|
||
| require 'ftools' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If ftools/fileutils is only used to create non-nested directories, then |
||
| require 'fileutils' | ||
| require 'instances.rb' | ||
|
|
||
| class PegSolitaire | ||
|
|
@@ -483,19 +483,19 @@ def pad(counter, maxCounter) | |
| def processConfig(configName, configNum, maxNum) | ||
| pegSolitaire = PegSolitaire.new(configName, $config_names[configNum-1]) | ||
|
|
||
| File.makedirs("tempo-sat") | ||
| FileUtils.mkdir_p("tempo-sat") | ||
| domainFile = File.new("tempo-sat/domain.pddl", "w") | ||
| pegSolitaire.printDomainTemporal(domainFile) | ||
| problemFile = File.new("tempo-sat/p#{pad(configNum,maxNum)}.pddl", "w") | ||
| pegSolitaire.printProblemTemporal(problemFile, pad(configNum,maxNum), pegSolitaire) | ||
|
|
||
| File.makedirs("seq-sat") | ||
| FileUtils.mkdir_p("seq-sat") | ||
| domainFile = File.new("seq-sat/domain.pddl", "w") | ||
| pegSolitaire.printDomainSequential(domainFile) | ||
| problemFile = File.new("seq-sat/p#{pad(configNum,maxNum)}.pddl", "w") | ||
| pegSolitaire.printProblemSequential(problemFile, pad(configNum,maxNum), pegSolitaire) | ||
|
|
||
| File.makedirs("netben-opt") | ||
| FileUtils.mkdir_p("netben-opt") | ||
| domainFile = File.new("netben-opt/domain.pddl", "w") | ||
| pegSolitaire.printDomainNetBenefit(domainFile) | ||
| problemFile1 = File.new("netben-opt/p#{pad(configNum,maxNum)}-1.pddl", "w") | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove the comment?