001package org.cpsolver.coursett.criteria; 002 003import java.util.List; 004 005import org.cpsolver.coursett.Constants; 006import org.cpsolver.coursett.constraint.RoomConstraint.RoomConstraintContext; 007import org.cpsolver.coursett.model.Placement; 008import org.cpsolver.coursett.model.TimeLocation; 009import org.cpsolver.ifs.util.DataProperties; 010 011 012/** 013 * Useless half-hours. This criterion counts cases when there is an empty half hour in a room. 014 * Such half-hours should be generally avoided as usually any class takes more than half an hour. 015 * <br> 016 * 017 * @author Tomáš Müller 018 * @version CourseTT 1.3 (University Course Timetabling)<br> 019 * Copyright (C) 2006 - 2014 Tomáš Müller<br> 020 * <a href="mailto:muller@unitime.org">muller@unitime.org</a><br> 021 * <a href="http://muller.unitime.org">http://muller.unitime.org</a><br> 022 * <br> 023 * This library is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU Lesser General Public License as 025 * published by the Free Software Foundation; either version 3 of the 026 * License, or (at your option) any later version. <br> 027 * <br> 028 * This library is distributed in the hope that it will be useful, but 029 * WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 031 * Lesser General Public License for more details. <br> 032 * <br> 033 * You should have received a copy of the GNU Lesser General Public 034 * License along with this library; if not see 035 * <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>. 036 */ 037public class UselessHalfHours extends BrokenTimePatterns { 038 039 @Override 040 public double getWeightDefault(DataProperties config) { 041 return Constants.sPreferenceLevelStronglyDiscouraged * config.getPropertyDouble("Comparator.UselessSlotWeight", 0.1); 042 } 043 044 @Override 045 public String getPlacementSelectionWeightName() { 046 return "Placement.UselessSlotsWeight"; 047 } 048 049 @Override 050 protected double penalty(RoomConstraintContext rc) { 051 return countUselessSlotsHalfHours(rc); 052 } 053 054 @Override 055 protected double penalty(RoomConstraintContext rc, Placement value) { 056 return countUselessSlotsHalfHours(rc, value); 057 } 058 059 private static boolean isEmpty(RoomConstraintContext rc, int slot, Placement placement) { 060 List<Placement> assigned = rc.getPlacements(slot); 061 return assigned.isEmpty() || (placement != null && assigned.size() == 1 && assigned.get(0).variable().equals(placement.variable())); 062 } 063 064 065 private static boolean isUselessBefore(RoomConstraintContext rc, int slot, Placement placement) { 066 int s = slot % Constants.SLOTS_PER_DAY; 067 if (s - 1 < 0 || s + 6 >= Constants.SLOTS_PER_DAY) 068 return false; 069 return (!isEmpty(rc, slot - 1, placement) && 070 isEmpty(rc, slot + 0, placement) && 071 isEmpty(rc, slot + 1, placement) && 072 isEmpty(rc, slot + 2, placement) && 073 isEmpty(rc, slot + 3, placement) && 074 isEmpty(rc, slot + 4, placement) && 075 isEmpty(rc, slot + 5, placement) && 076 isEmpty(rc, slot + 6, placement)); 077 } 078 079 private static boolean isUselessAfter(RoomConstraintContext rc, int slot, Placement placement) { 080 int s = slot % Constants.SLOTS_PER_DAY; 081 if (s - 1 < 0 || s + 6 >= Constants.SLOTS_PER_DAY) 082 return false; 083 return (isEmpty(rc, slot - 1, placement) && 084 isEmpty(rc, slot + 0, placement) && 085 isEmpty(rc, slot + 1, placement) && 086 isEmpty(rc, slot + 2, placement) && 087 isEmpty(rc, slot + 3, placement) && 088 isEmpty(rc, slot + 4, placement) && 089 isEmpty(rc, slot + 5, placement) && 090 !isEmpty(rc, slot + 6, placement)); 091 } 092 093 private static boolean isUseless(RoomConstraintContext rc, int slot, Placement placement) { 094 int s = slot % Constants.SLOTS_PER_DAY; 095 if (s - 1 < 0 || s + 6 >= Constants.SLOTS_PER_DAY) 096 return false; 097 return (!isEmpty(rc, slot - 1, placement) && 098 isEmpty(rc, slot + 0, placement) && 099 isEmpty(rc, slot + 1, placement) && 100 isEmpty(rc, slot + 2, placement) && 101 isEmpty(rc, slot + 3, placement) && 102 isEmpty(rc, slot + 4, placement) && 103 isEmpty(rc, slot + 5, placement) && 104 !isEmpty(rc, slot + 6, placement)); 105 } 106 107 /** Number of useless half hours for this room 108 * @param rc room constraint assignment context 109 * @param placement placement that is being considered 110 * @return number of useless slots caused by the given placement 111 **/ 112 protected static int countUselessSlotsHalfHours(RoomConstraintContext rc, Placement placement) { 113 int ret = 0; 114 TimeLocation time = placement.getTimeLocation(); 115 int slot = time.getStartSlot() % Constants.SLOTS_PER_DAY; 116 int days = time.getDayCode(); 117 for (int d = 0; d < Constants.NR_DAYS; d++) { 118 if ((Constants.DAY_CODES[d] & days) == 0) 119 continue; 120 if (isUselessBefore(rc, d * Constants.SLOTS_PER_DAY + slot - 6, placement)) 121 ret ++; 122 if (isUselessAfter(rc, d * Constants.SLOTS_PER_DAY + slot + time.getNrSlotsPerMeeting(), placement)) 123 ret ++; 124 if (time.getNrSlotsPerMeeting() == 6 && isUseless(rc, d * Constants.SLOTS_PER_DAY + slot, placement)) 125 ret --; 126 } 127 return ret; 128 } 129 130 private static boolean isUseless(RoomConstraintContext rc, int slot) { 131 int s = slot % Constants.SLOTS_PER_DAY; 132 if (s - 1 < 0 || s + 6 >= Constants.SLOTS_PER_DAY) 133 return false; 134 return (!rc.getPlacements(slot - 1).isEmpty() && 135 rc.getPlacements(slot + 0).isEmpty() && 136 rc.getPlacements(slot + 1).isEmpty() && 137 rc.getPlacements(slot + 2).isEmpty() && 138 rc.getPlacements(slot + 3).isEmpty() && 139 rc.getPlacements(slot + 4).isEmpty() && 140 rc.getPlacements(slot + 5).isEmpty() && 141 !rc.getPlacements(slot + 6).isEmpty()); 142 } 143 144 /** Number of useless slots for this room 145 * @param rc room constraint assignment context 146 * @return current penalty for the given room 147 **/ 148 public static int countUselessSlotsHalfHours(RoomConstraintContext rc) { 149 int ret = 0; 150 for (int d = 0; d < Constants.NR_DAYS; d++) { 151 for (int s = 0; s < Constants.SLOTS_PER_DAY; s++) { 152 int slot = d * Constants.SLOTS_PER_DAY + s; 153 if (isUseless(rc, slot)) 154 ret++; 155 } 156 } 157 return ret; 158 } 159}