Commits

Luke Plant  committed 1fbde86

Allow table count to be specified exactly, to increase flexibility of searches

  • Participants
  • Parent commits a41f370

Comments (0)

Files changed (4)

File seating_planner.py

     plan.remove(t2)
     p1 = random.choice(t1)
     p2 = random.choice(t2)
-    t1 = Table(p for p in t1 if p != p1) + Table([p2])
-    t2 = Table(p for p in t2 if p != p2) + Table([p1])
+
+    t1 = list(t1)
+    t1.remove(p1)
+    t1.append(p2)
+    t1 = Table(t1)
+
+    t2 = list(t2)
+    t2.remove(p2)
+    t2.append(p1)
+    t2 = Table(t2)
+
     plan.append(t1)
     plan.append(t2)
     return plan
 MAX_CONNECTION = 50
 
 class PlanningData(object):
-    def __init__(self, names, connections, table_size):
+    def __init__(self, names, connections, table_size, table_count):
         self.NAMES = names
         self.CONNECTIONS = connections
         for row in connections:
 
         self.TABLE_SIZE = table_size
         self.PEOPLE_COUNT = len(names)
-        self.TABLE_COUNT = int(math.ceil(self.PEOPLE_COUNT/self.TABLE_SIZE))
+        self.TABLE_COUNT = table_count
         # Just need a value that is big enough to keep energy always positive
         self.MAX_ENERGY = MAX_CONNECTION * self.TABLE_COUNT * self.PEOPLE_COUNT**2
 
     def get_initial_plan(self):
         people = range(0, self.PEOPLE_COUNT)
         random.shuffle(people)
-        return Plan(Table(people[i::self.TABLE_COUNT]) for i in range(self.TABLE_COUNT))
+
+        # We need extra items, to represent spare places.  This allows us the
+        # flexibility to have different sized tables.  To avoid fruitless
+        # searches, we put them at the end *after* shuffling, and group such
+        # that we get empty tables at the end.
+        total_places = self.TABLE_SIZE * self.TABLE_COUNT
+
+        people.extend([None] * (total_places - len(people)))
+
+        s = self.TABLE_SIZE
+        c = self.TABLE_COUNT
+        return Plan(Table(people[i*s:(i+1)*s]) for i in range(c))
 
     def energy(self, plan):
         val = sum(
         return self.MAX_ENERGY - val
 
     def plan_to_names(self, plan):
-        return Plan(Table(self.NAMES[p] for p in table)
+        return Plan(Table(self.NAMES[p] for p in table
+                          if p is not None)
                     for table in plan)
 
 
 
-def solve(names, connections, table_size):
-    planning_data = PlanningData(names, connections, table_size)
+def solve(names, connections, table_size, table_count):
+    planning_data = PlanningData(names, connections, table_size, table_count)
     state = planning_data.get_initial_plan()
     annealer = Annealer(planning_data.energy, move)
     schedule = annealer.auto(state, minutes=0.1, steps=100)
                                schedule['tmax'], schedule['tmin'],
                                schedule['steps'], updates=6)
 
+    # Remove empty tables:
+    state = [t for t in state if any(p is not None for p in t)]
     return planning_data, state
 
 
 if __name__ == '__main__':
-    planning_data, plan = solve(EXAMPLE_NAMES, EXAMPLE_CONNECTIONS, 9)
+    planning_data, plan = solve(EXAMPLE_NAMES, EXAMPLE_CONNECTIONS, 9, 2)
     print normalise_plan(planning_data.plan_to_names(plan))

File static/plans.js

             return;
         }
         tableSize = parseInt(tableSize, 10);
+
         var nameCount = connectionsMatrix.getData().length - 1;
         if (tableSize >= nameCount) {
             alert("Tables must be smaller than the number of people");
             return;
         }
 
+        var tableCount = $('#table-count').val();
+        if (! tableCount.match(/^\d+$/)) {
+            alert("Please enter an integer for number of tables");
+            return;
+        }
+        tableCount = parseInt(tableCount, 10);
+
+        if (tableCount * tableSize < nameCount) {
+            alert("You just don't have enough tables. This is never going to work...");
+            return;
+        }
+
         $("#loading").show();
         $.ajax({
             url: $SCRIPT_ROOT + "/find-solution/",
             type: 'POST',
             data: {
                 connections: getRawConnectionsData(),
+                tableCount: tableCount,
                 tableSize: tableSize
             },
             success: showSolution,

File templates/index.html

 
     <h2>Parameters</h2>
 
-    <p><label>Table size <input type="text" id="table-size" value="5"></label></p>
+    <p><label>Table size <input type="text" id="table-size" value="8"></label></p>
+    <p><label>Number of tables <input type="text" id="table-count" value="5"></label></p>
 
     <h2>Plans</h2>
 
     # Parse data:
     connections = request.form.get('connections', "")
     table_size = request.form.get('tableSize', 0, type=int)
+    table_count = request.form.get('tableCount', 0, type=int)
     rows = connections.strip("\n").split("\n")
     d = []
     for row in rows:
         matrix.append(line)
 
     # Solve
-    planning_data, plan = solve(names, matrix, table_size)
+    planning_data, plan = solve(names, matrix, table_size, table_count)
 
     return jsonify({'solution': normalise_plan(planning_data.plan_to_names(plan))})