001    package net.sf.cpsolver.exam.neighbours;
002    
003    import java.util.Iterator;
004    import java.util.Set;
005    
006    import net.sf.cpsolver.exam.model.Exam;
007    import net.sf.cpsolver.exam.model.ExamModel;
008    import net.sf.cpsolver.exam.model.ExamPeriodPlacement;
009    import net.sf.cpsolver.exam.model.ExamPlacement;
010    import net.sf.cpsolver.exam.model.ExamRoomPlacement;
011    import net.sf.cpsolver.ifs.heuristics.NeighbourSelection;
012    import net.sf.cpsolver.ifs.model.Neighbour;
013    import net.sf.cpsolver.ifs.solution.Solution;
014    import net.sf.cpsolver.ifs.solver.Solver;
015    import net.sf.cpsolver.ifs.util.DataProperties;
016    import net.sf.cpsolver.ifs.util.ToolBox;
017    
018    /**
019     * A new period is selected for a randomly selected exam. It tries to use the
020     * current set of rooms, if it is possible (exam is assigned, rooms are
021     * available and not used during the new period). Otherwise, rooms are selected
022     * using {@link Exam#findBestAvailableRooms(ExamPeriodPlacement)}. <br>
023     * <br>
024     * 
025     * @version ExamTT 1.2 (Examination Timetabling)<br>
026     *          Copyright (C) 2008 - 2010 Tomas Muller<br>
027     *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
028     *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
029     * <br>
030     *          This library is free software; you can redistribute it and/or modify
031     *          it under the terms of the GNU Lesser General Public License as
032     *          published by the Free Software Foundation; either version 3 of the
033     *          License, or (at your option) any later version. <br>
034     * <br>
035     *          This library is distributed in the hope that it will be useful, but
036     *          WITHOUT ANY WARRANTY; without even the implied warranty of
037     *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
038     *          Lesser General Public License for more details. <br>
039     * <br>
040     *          You should have received a copy of the GNU Lesser General Public
041     *          License along with this library; if not see
042     *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
043     */
044    public class ExamTimeMove implements NeighbourSelection<Exam,ExamPlacement> {
045        private boolean iCheckStudentConflicts = false;
046        private boolean iCheckDistributionConstraints = true;
047        
048        /**
049         * Constructor
050         * @param properties problem properties
051         */
052        public ExamTimeMove(DataProperties properties) {
053            iCheckStudentConflicts = properties.getPropertyBoolean("ExamTimeMove.CheckStudentConflicts", iCheckStudentConflicts);
054            iCheckDistributionConstraints = properties.getPropertyBoolean("ExamTimeMove.CheckDistributionConstraints", iCheckDistributionConstraints);
055        }
056        
057        /**
058         * Initialization
059         */
060        @Override
061        public void init(Solver<Exam,ExamPlacement> solver) {}
062        
063        /**
064         * Select an exam randomly,
065         * select an available period randomly (if it is not assigned), 
066         * use rooms if possible, select rooms using {@link Exam#findBestAvailableRooms(ExamPeriodPlacement)} if not (exam is unassigned, a room is not available or used).
067         */
068        @Override
069        public Neighbour<Exam,ExamPlacement> selectNeighbour(Solution<Exam,ExamPlacement> solution) {
070            ExamModel model = (ExamModel)solution.getModel();
071            Exam exam = ToolBox.random(model.variables());
072            ExamPlacement placement = exam.getAssignment();
073            int px = ToolBox.random(exam.getPeriodPlacements().size());
074            for (int p=0;p<exam.getPeriodPlacements().size();p++) {
075                ExamPeriodPlacement period = exam.getPeriodPlacements().get((p+px)%exam.getPeriodPlacements().size());
076                if (placement!=null && placement.getPeriod().equals(period)) continue;
077                if (iCheckStudentConflicts && exam.countStudentConflicts(period)>0) continue;
078                if (iCheckDistributionConstraints && !exam.checkDistributionConstraints(period)) continue;
079                if (placement!=null) {
080                    boolean ok = true;
081                    for (Iterator<ExamRoomPlacement> i=placement.getRoomPlacements().iterator();i.hasNext();) {
082                        ExamRoomPlacement room = i.next();
083                        if (!room.isAvailable(period.getPeriod()) || room.getRoom().getPlacement(period.getPeriod())!=null) {
084                            ok = false; break;
085                        }
086                    }
087                    if (ok)
088                        return new ExamSimpleNeighbour(new ExamPlacement(exam, period, placement.getRoomPlacements()));
089                }
090                Set<ExamRoomPlacement> rooms = exam.findBestAvailableRooms(period);
091                if (rooms==null) continue;
092                return new ExamSimpleNeighbour(new ExamPlacement(exam, period, rooms));
093            }
094            return null;
095        }
096    }