|
@@ -0,0 +1,609 @@
|
|
|
+package schedule
|
|
|
+
|
|
|
+import (
|
|
|
+ "os"
|
|
|
+ "sort"
|
|
|
+ "time"
|
|
|
+ "errors"
|
|
|
+ "math/rand"
|
|
|
+ "encoding/json"
|
|
|
+)
|
|
|
+
|
|
|
+func init() {
|
|
|
+ rand.Seed(time.Now().UnixNano())
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) tryGenerate(
|
|
|
+ subgroup SubgroupType,
|
|
|
+ subjtype SubjectType,
|
|
|
+ group *Group,
|
|
|
+ subject *Subject,
|
|
|
+ schedule *Schedule,
|
|
|
+ countl *Subgroup,
|
|
|
+ template []*Schedule,
|
|
|
+) {
|
|
|
+ flag := false
|
|
|
+startAgain:
|
|
|
+ nextLesson: for lesson := uint(0); lesson < NUM_TABLES; lesson++ {
|
|
|
+ // Если лимит пар на день окончен, тогда прекратить ставить пары группе.
|
|
|
+ if gen.isLimitLessons(subgroup, countl) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если это полная группа и у неё не осталось теоретических занятий, тогда пропустить этот предмет.
|
|
|
+ if subjtype == THEORETICAL && !gen.haveTheoreticalLessons(subject) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если это подгруппа и у неё не осталось практических занятий, тогда пропустить этот предмет.
|
|
|
+ if subjtype == PRACTICAL && !gen.havePracticalLessons(subgroup, subject) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если учитель заблокирован (не может присутствовать на занятиях) или
|
|
|
+ // лимит пар на текущую неделю для предмета завершён, тогда пропустить этот предмет.
|
|
|
+ if gen.inBlocked(subject.Teacher) || gen.notHaveWeekLessons(subgroup, subject) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ isAfter := false
|
|
|
+ savedLesson := lesson
|
|
|
+
|
|
|
+ // [ I ] Первая проверка.
|
|
|
+ switch subgroup {
|
|
|
+ case ALL:
|
|
|
+ // Если две подгруппы стоят друг за другом, тогда исключить возможность добавления полной пары.
|
|
|
+ for i := uint(0); i < NUM_TABLES-1; i++ {
|
|
|
+ if gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(B, schedule, i) &&
|
|
|
+ gen.cellIsReserved(B, schedule, i+1) && !gen.cellIsReserved(A, schedule, i+1) ||
|
|
|
+ gen.cellIsReserved(B, schedule, i) && !gen.cellIsReserved(A, schedule, i) &&
|
|
|
+ gen.cellIsReserved(A, schedule, i+1) && !gen.cellIsReserved(B, schedule, i+1) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если между двумя разными подгруппами находятся окна, тогда посчитать сколько пустых окон.
|
|
|
+ // Если их количество = 1, тогда попытаться подставить полную пару под это окно.
|
|
|
+ // Если не получается, тогда не ставить полную пару.
|
|
|
+ cellSubgroupReserved := false
|
|
|
+ indexNotReserved := uint(0)
|
|
|
+ cellIsNotReserved := 0
|
|
|
+ for i := uint(0); i < NUM_TABLES; i++ {
|
|
|
+ if gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(B, schedule, i) ||
|
|
|
+ gen.cellIsReserved(B, schedule, i) && !gen.cellIsReserved(A, schedule, i) {
|
|
|
+ if cellIsNotReserved != 0 {
|
|
|
+ if cellIsNotReserved == 1 {
|
|
|
+ lesson = indexNotReserved
|
|
|
+ goto tryAfter
|
|
|
+ }
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ cellSubgroupReserved = true
|
|
|
+ }
|
|
|
+ if !gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(B, schedule, i) && cellSubgroupReserved {
|
|
|
+ if cellIsNotReserved == 0 {
|
|
|
+ indexNotReserved = i
|
|
|
+ }
|
|
|
+ cellIsNotReserved += 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ switch {
|
|
|
+ case lesson > 1 && (gen.cellIsReserved(A, schedule, lesson-1) || gen.cellIsReserved(B, schedule, lesson-1)) &&
|
|
|
+ !(gen.cellIsReserved(A, schedule, lesson+1) || gen.cellIsReserved(B, schedule, lesson+1)): // pass
|
|
|
+ default:
|
|
|
+ // "Подтягивать" полные пары к уже существующим [перед].
|
|
|
+ for i := uint(0); i < NUM_TABLES-1; i++ {
|
|
|
+ if (gen.cellIsReserved(A, schedule, i+1) || gen.cellIsReserved(B, schedule, i+1)) &&
|
|
|
+ (!gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(B, schedule, i)) {
|
|
|
+ lesson = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ switch {
|
|
|
+ case lesson > 1 && gen.cellIsReserved(subgroup, schedule, lesson-1) &&
|
|
|
+ !gen.cellIsReserved(subgroup, schedule, lesson+1): // pass
|
|
|
+ default:
|
|
|
+ // "Подтягивать" неполные пары к уже существующим [перед].
|
|
|
+ for i := uint(0); i < NUM_TABLES-1; i++ {
|
|
|
+ if !gen.cellIsReserved(subgroup, schedule, i) && gen.cellIsReserved(subgroup, schedule, i+1) {
|
|
|
+ lesson = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+tryAfter:
|
|
|
+ if isAfter {
|
|
|
+ switch subgroup {
|
|
|
+ case ALL:
|
|
|
+ // "Подтягивать" полные пары к уже существующим [после].
|
|
|
+ for i := uint(0); i < NUM_TABLES-1; i++ {
|
|
|
+ if (gen.cellIsReserved(A, schedule, i) || gen.cellIsReserved(B, schedule, i)) &&
|
|
|
+ (!gen.cellIsReserved(A, schedule, i+1) && !gen.cellIsReserved(B, schedule, i+1)) {
|
|
|
+ lesson = i+1
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ // "Подтягивать" неполные пары к уже существующим [после].
|
|
|
+ for i := uint(0); i < NUM_TABLES-1; i++ {
|
|
|
+ if (gen.cellIsReserved(ALL, schedule, i) || gen.cellIsReserved(subgroup, schedule, i)) &&
|
|
|
+ !gen.cellIsReserved(subgroup, schedule, i+1) {
|
|
|
+ lesson = i+1
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ var (
|
|
|
+ cabinet = ""
|
|
|
+ cabinet2 = ""
|
|
|
+ )
|
|
|
+ // Если ячейка уже зарезервирована или учитель занят, или кабинет зарезервирован, или
|
|
|
+ // если это полная пара и ячейка занята либо первой, либо второй подгруппой, или
|
|
|
+ // если это двойная пара и кабинеты второго учителя зарезервированы, или
|
|
|
+ // если это двойная пара и второй учитель занят: попытаться сдвинуть пары к уже существующим.
|
|
|
+ // Если не получается, тогда не ставить этот урок.
|
|
|
+ if gen.cellIsReserved(subgroup, schedule, lesson) ||
|
|
|
+ gen.teacherIsReserved(subject.Teacher, lesson) ||
|
|
|
+ gen.cabinetIsReserved(subgroup, subject, subject.Teacher, lesson, &cabinet) ||
|
|
|
+ (subgroup == ALL && (gen.cellIsReserved(A, schedule, lesson) || gen.cellIsReserved(B, schedule, lesson))) ||
|
|
|
+ (gen.withSubgroups(group.Name) && gen.isDoubleLesson(group.Name, subject.Name) && gen.teacherIsReserved(subject.Teacher2, lesson)) ||
|
|
|
+ (gen.withSubgroups(group.Name) && gen.isDoubleLesson(group.Name, subject.Name) && gen.cabinetIsReserved(subgroup, subject, subject.Teacher2, lesson, &cabinet2)) {
|
|
|
+ if isAfter {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ if lesson != savedLesson {
|
|
|
+ isAfter = true
|
|
|
+ goto tryAfter
|
|
|
+ }
|
|
|
+ continue nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ // Если существует шаблон расписания, тогда придерживаться его структуры.
|
|
|
+ if template != nil {
|
|
|
+ for _, sch := range template {
|
|
|
+ if sch.Group == group.Name {
|
|
|
+ if sch.Table[lesson].Subject[A] == "" && sch.Table[lesson].Subject[B] == "" {
|
|
|
+ if (gen.cellIsReserved(A, schedule, lesson) && !gen.cellIsReserved(B, schedule, lesson)) ||
|
|
|
+ (gen.cellIsReserved(B, schedule, lesson) && !gen.cellIsReserved(A, schedule, lesson)) {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ lesson = savedLesson
|
|
|
+ continue nextLesson
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Полный день - максимум 7 пар.
|
|
|
+ // lesson начинается с нуля!
|
|
|
+ if (gen.Day != WEDNESDAY && gen.Day != SATURDAY) && lesson >= 7 {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ // В среду и субботу - 5-6 пары должны быть свободны.
|
|
|
+ // lesson начинается с нуля!
|
|
|
+ if (gen.Day == WEDNESDAY || gen.Day == SATURDAY) && (lesson == 4 || lesson == 5) {
|
|
|
+ lesson = savedLesson // Возобновить сохранённое занятие.
|
|
|
+ continue nextLesson // Перейти к следующей ячейке.
|
|
|
+ }
|
|
|
+
|
|
|
+ // [ II ] Вторая проверка.
|
|
|
+ switch subgroup {
|
|
|
+ case ALL:
|
|
|
+ // Если уже существует полная пара, которая стоит за парами с подгруппами, тогда
|
|
|
+ // перейти на новую ячейку расписания группы.
|
|
|
+ for i := lesson; i < NUM_TABLES-1; i++ {
|
|
|
+ if gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(B, schedule, i) && gen.cellIsReserved(ALL, schedule, i+1) ||
|
|
|
+ gen.cellIsReserved(B, schedule, i) && !gen.cellIsReserved(A, schedule, i) && gen.cellIsReserved(ALL, schedule, i+1) {
|
|
|
+ lesson = savedLesson
|
|
|
+ continue nextLesson
|
|
|
+ }
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ // Если у одной подгруппы уже имеется пара, а у второй стоит пара
|
|
|
+ // в это же время, тогда пропустить проверку пустых окон.
|
|
|
+ if gen.cellIsReserved(A, schedule, lesson) && A != subgroup ||
|
|
|
+ gen.cellIsReserved(B, schedule, lesson) && B != subgroup {
|
|
|
+ goto passcheck
|
|
|
+ }
|
|
|
+ // Если стоит полная пара, а за ней идёт подгруппа неравная проверяемой, тогда
|
|
|
+ // прекратить ставить пары у проверяемой подгруппы.
|
|
|
+ fullLessons := false
|
|
|
+ for i := uint(0); i < lesson; i++ {
|
|
|
+ if gen.cellIsReserved(ALL, schedule, i) {
|
|
|
+ fullLessons = true
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if (fullLessons && gen.cellIsReserved(A, schedule, i) && A != subgroup) ||
|
|
|
+ (fullLessons && gen.cellIsReserved(B, schedule, i) && B != subgroup) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+passcheck:
|
|
|
+ // [ III ] Третья проверка.
|
|
|
+ // Если нет возможности добавить новые пары без создания окон, тогда не ставить пары.
|
|
|
+ if lesson > 1 {
|
|
|
+ switch subgroup {
|
|
|
+ case ALL:
|
|
|
+ for i := uint(0); i < lesson-1; i++ {
|
|
|
+ if (gen.cellIsReserved(A, schedule, i) && !gen.cellIsReserved(A, schedule, lesson-1)) ||
|
|
|
+ (gen.cellIsReserved(B, schedule, i) && !gen.cellIsReserved(B, schedule, lesson-1)) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ for i := uint(0); i < lesson-1; i++ {
|
|
|
+ if gen.cellIsReserved(subgroup, schedule, i) && !gen.cellIsReserved(subgroup, schedule, lesson-1) {
|
|
|
+ break nextLesson
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ gen.Reserved.Teachers[subject.Teacher][lesson] = true
|
|
|
+ gen.Reserved.Cabinets[cabinet][lesson] = true
|
|
|
+
|
|
|
+ switch subgroup {
|
|
|
+ case A:
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.A -= 1
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].Practice.A -= 1
|
|
|
+ countl.A += 1
|
|
|
+ case B:
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.B -= 1
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].Practice.B -= 1
|
|
|
+ countl.B += 1
|
|
|
+ case ALL:
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.A -= 1
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].WeekLessons.B -= 1
|
|
|
+ countl.A += 1
|
|
|
+ countl.B += 1
|
|
|
+ switch subjtype {
|
|
|
+ case THEORETICAL:
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].Theory -= 1
|
|
|
+ case PRACTICAL:
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].Practice.A -= 1
|
|
|
+ gen.Groups[group.Name].Subjects[subject.Name].Practice.B -= 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if subgroup == ALL {
|
|
|
+ schedule.Table[lesson].Teacher = [ALL]string{
|
|
|
+ subject.Teacher,
|
|
|
+ subject.Teacher,
|
|
|
+ }
|
|
|
+ schedule.Table[lesson].Subject = [ALL]string{
|
|
|
+ subject.Name,
|
|
|
+ subject.Name,
|
|
|
+ }
|
|
|
+ schedule.Table[lesson].Cabinet = [ALL]string{
|
|
|
+ cabinet,
|
|
|
+ cabinet,
|
|
|
+ }
|
|
|
+ // Если это двойная пара и группа делимая, тогда поставить пару с разными преподавателями.
|
|
|
+ if gen.isDoubleLesson(group.Name, subject.Name) && gen.withSubgroups(group.Name) {
|
|
|
+ gen.Reserved.Teachers[subject.Teacher2][lesson] = true
|
|
|
+ gen.Reserved.Cabinets[cabinet2][lesson] = true
|
|
|
+ schedule.Table[lesson].Teacher[B] = subject.Teacher2
|
|
|
+ schedule.Table[lesson].Cabinet[B] = cabinet2
|
|
|
+ }
|
|
|
+ lesson = savedLesson
|
|
|
+ continue nextLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ schedule.Table[lesson].Teacher[subgroup] = subject.Teacher
|
|
|
+ schedule.Table[lesson].Subject[subgroup] = subject.Name
|
|
|
+ schedule.Table[lesson].Cabinet[subgroup] = cabinet
|
|
|
+ lesson = savedLesson
|
|
|
+ }
|
|
|
+
|
|
|
+ if template != nil && !flag {
|
|
|
+ flag = true
|
|
|
+ goto startAgain
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) isLimitLessons(subgroup SubgroupType, countl *Subgroup) bool {
|
|
|
+ switch subgroup {
|
|
|
+ case ALL:
|
|
|
+ if countl.A >= MAX_COUNT_LESSONS_IN_DAY && countl.B >= MAX_COUNT_LESSONS_IN_DAY {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case A:
|
|
|
+ if countl.A >= MAX_COUNT_LESSONS_IN_DAY {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case B:
|
|
|
+ if countl.B >= MAX_COUNT_LESSONS_IN_DAY {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) withSubgroups(group string) bool {
|
|
|
+ if gen.Groups[group].Quantity > MAX_COUNT_WITHOUT_SUBGROUPS {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) blockTeacher(teacher string) error {
|
|
|
+ if !gen.inTeachers(teacher) {
|
|
|
+ return errors.New("teacher undefined")
|
|
|
+ }
|
|
|
+ gen.Blocked[teacher] = true
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) inBlocked(teacher string) bool {
|
|
|
+ if _, ok := gen.Blocked[teacher]; !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) inGroups(group string) bool {
|
|
|
+ if _, ok := gen.Groups[group]; !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) inTeachers(teacher string) bool {
|
|
|
+ if _, ok := gen.Teachers[teacher]; !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) unblockTeacher(teacher string) error {
|
|
|
+ if !gen.inBlocked(teacher) {
|
|
|
+ return errors.New("teacher undefined")
|
|
|
+ }
|
|
|
+ delete(gen.Blocked, teacher)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func packJSON(data interface{}) []byte {
|
|
|
+ jsonData, err := json.MarshalIndent(data, "", "\t")
|
|
|
+ if err != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ return jsonData
|
|
|
+}
|
|
|
+
|
|
|
+func unpackJSON(data []byte, output interface{}) error {
|
|
|
+ err := json.Unmarshal(data, output)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func writeJSON(filename string, data interface{}) error {
|
|
|
+ return writeFile(filename, string(packJSON(data)))
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) subjectInGroup(subject string, group string) bool {
|
|
|
+ if !gen.inGroups(group) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if _, ok := gen.Groups[group].Subjects[subject]; !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) isDoubleLesson(group string, subject string) bool {
|
|
|
+ if !gen.inGroups(group) {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if _, ok := gen.Groups[group].Subjects[subject]; !ok {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ if gen.Groups[group].Subjects[subject].Teacher2 == "" {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func shuffle(slice interface{}) interface{}{
|
|
|
+ switch slice.(type) {
|
|
|
+ case []*Group:
|
|
|
+ result := slice.([]*Group)
|
|
|
+ for i := len(result)-1; i > 0; i-- {
|
|
|
+ j := rand.Intn(i+1)
|
|
|
+ result[i], result[j] = result[j], result[i]
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ case []*Subject:
|
|
|
+ result := slice.([]*Subject)
|
|
|
+ for i := len(result)-1; i > 0; i-- {
|
|
|
+ j := rand.Intn(i+1)
|
|
|
+ result[i], result[j] = result[j], result[i]
|
|
|
+ }
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) cellIsReserved(subgroup SubgroupType, schedule *Schedule, lesson uint) bool {
|
|
|
+ if lesson >= NUM_TABLES {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ switch subgroup {
|
|
|
+ case A:
|
|
|
+ if schedule.Table[lesson].Subject[A] != "" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case B:
|
|
|
+ if schedule.Table[lesson].Subject[B] != "" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case ALL:
|
|
|
+ if schedule.Table[lesson].Subject[A] != "" && schedule.Table[lesson].Subject[B] != "" {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) cabinetIsReserved(subgroup SubgroupType, subject *Subject, teacher string, lesson uint, cabinet *string) bool {
|
|
|
+ var result = true
|
|
|
+ if !gen.inTeachers(teacher) {
|
|
|
+ return result
|
|
|
+ }
|
|
|
+ for _, cab := range gen.Teachers[teacher].Cabinets {
|
|
|
+ gen.cabinetToReserved(cab.Name)
|
|
|
+ // Если это не компьютерный кабинет, а предмет предполагает практику в компьютерных кабинетах и идёт время практики,
|
|
|
+ // тогда посмотреть другой кабинет преподавателя.
|
|
|
+ if subject.IsComputer && !cab.IsComputer &&
|
|
|
+ gen.havePracticalLessons(subgroup, subject) &&
|
|
|
+ !gen.haveTheoreticalLessons(subject) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // Если кабинет не зарезирвирован, тогда занять кабинет.
|
|
|
+ if _, ok := gen.Reserved.Cabinets[cab.Name]; ok && !gen.Reserved.Cabinets[cab.Name][lesson] {
|
|
|
+ *cabinet = cab.Name
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) teacherIsReserved(teacher string, lesson uint) bool {
|
|
|
+ gen.teacherToReserved(teacher)
|
|
|
+ if value, ok := gen.Reserved.Teachers[teacher]; ok {
|
|
|
+ return value[lesson] == true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) teacherToReserved(teacher string) {
|
|
|
+ if _, ok := gen.Reserved.Teachers[teacher]; ok {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ gen.Reserved.Teachers[teacher] = make([]bool, NUM_TABLES)
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) cabinetToReserved(cabnum string) {
|
|
|
+ if _, ok := gen.Reserved.Cabinets[cabnum]; ok {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ gen.Reserved.Cabinets[cabnum] = make([]bool, NUM_TABLES)
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) notHaveWeekLessons(subgroup SubgroupType, subject *Subject) bool {
|
|
|
+ switch subgroup {
|
|
|
+ case A:
|
|
|
+ if subject.WeekLessons.A == 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case B:
|
|
|
+ if subject.WeekLessons.B == 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ case ALL:
|
|
|
+ if subject.WeekLessons.A == 0 && subject.WeekLessons.B == 0 {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) haveTheoreticalLessons(subject *Subject) bool {
|
|
|
+ if subject.Theory == 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func (gen *Generator) havePracticalLessons(subgroup SubgroupType, subject *Subject) bool {
|
|
|
+ switch subgroup {
|
|
|
+ case A:
|
|
|
+ if subject.Practice.A == 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ case B:
|
|
|
+ if subject.Practice.B == 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ case ALL:
|
|
|
+ if subject.Practice.A == 0 && subject.Practice.B == 0 {
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true
|
|
|
+}
|
|
|
+
|
|
|
+func getGroups(groups map[string]*Group) []*Group {
|
|
|
+ var list []*Group
|
|
|
+ for _, group := range groups {
|
|
|
+ list = append(list, group)
|
|
|
+ }
|
|
|
+ return shuffle(list).([]*Group)
|
|
|
+}
|
|
|
+
|
|
|
+func getSubjects(subjects map[string]*Subject) []*Subject {
|
|
|
+ var list []*Subject
|
|
|
+ for _, subject := range subjects {
|
|
|
+ list = append(list, subject)
|
|
|
+ }
|
|
|
+ return shuffle(list).([]*Subject)
|
|
|
+}
|
|
|
+
|
|
|
+func sortSchedule(schedule []*Schedule) []*Schedule {
|
|
|
+ sort.SliceStable(schedule, func(i, j int) bool {
|
|
|
+ return schedule[i].Group < schedule[j].Group
|
|
|
+ })
|
|
|
+ return schedule
|
|
|
+}
|
|
|
+
|
|
|
+func colWidthForCabinets(index int) (int, int, float64) {
|
|
|
+ var col = (index+1)*3+1
|
|
|
+ return col, col, COL_W_CAB
|
|
|
+}
|
|
|
+
|
|
|
+// Returns [min:max] value.
|
|
|
+func random(min, max int) int {
|
|
|
+ return rand.Intn(max - min + 1) + min
|
|
|
+}
|
|
|
+
|
|
|
+func readFile(filename string) string {
|
|
|
+ file, err := os.Open(filename)
|
|
|
+ if err != nil {
|
|
|
+ return ""
|
|
|
+ }
|
|
|
+ defer file.Close()
|
|
|
+
|
|
|
+ var (
|
|
|
+ buffer []byte = make([]byte, BUFFER)
|
|
|
+ data string
|
|
|
+ )
|
|
|
+
|
|
|
+ for {
|
|
|
+ length, err := file.Read(buffer)
|
|
|
+ if length == 0 || err != nil { break }
|
|
|
+ data += string(buffer[:length])
|
|
|
+ }
|
|
|
+
|
|
|
+ return data
|
|
|
+}
|
|
|
+
|
|
|
+func writeFile(filename, data string) error {
|
|
|
+ file, err := os.Create(filename)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ file.WriteString(data)
|
|
|
+ file.Close()
|
|
|
+ return nil
|
|
|
+}
|