#!/usr/bin/ruby require 'AbstractConstraint' require 'extensions' module ConstraintSolver # Represents a one-of-equals constraint. class OneOfEqualsConstraint < AbstractConstraint attr_reader :variables, :value, :violationCost # Initialises a new one-of-equals constraint. # The arguments are the list of variables involved in the constraint and # the value at least one of the variables should be equal to. # Optionally, a cost for violating the constraint can be specified. def initialize(variables, value, violationCost=1) if variables.empty? raise ArgumentError, "List of variables must not be empty!" end @variables = variables @value = value @violationCost = violationCost end def holds? if not allAssigned? or not @variables.find { |var| var.value == @value }.nil? return true else return false end end def allAssigned? @variables.each { |var| return false if not var.assigned? } return true end def include?(variable) @variables.include?(variable) end def to_s @variables.collect { |var| var.name }.join(" != ") end def to_s_full @variables.collect { |var| var.to_s }.join(" != ") end def ==(constraint) return false unless constraint.kind_of?(OneOfEqualsConstraint) (@variables == constraint.variables) and (@value == constraint.value) and (@violationCost == constraint.violationCost) end def each @variables.each { |var| yield var } end def revise checks = 0 revisedVariables = [] wipeout = false if @variables.find_all { |var| var.assigned? and not var.value == @value }.size == @variables.size - 1 unassigned = @variables.find { |var| not var.assigned? } pruneList = unassigned.domain.values - [ @value ] if not pruneList.empty? begin unassigned.domain.prune(pruneList) rescue DomainWipeoutException wipeout = true end revisedVariables << unassigned end end return revisedVariables, checks, wipeout end end end