From f8cf315e350888d5b1180d63b0b0093fc5a36f98 Mon Sep 17 00:00:00 2001 From: Denis Mukhametov Date: Thu, 19 Oct 2017 09:59:24 +0500 Subject: [PATCH 1/2] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D0=BB=20=D1=80=D0=B5=D0=BF=D0=BE=D0=B7=D0=B8=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D0=B8=D0=B9=202016-=D0=B3=D0=BE=20=D1=81=D0=BD=D0=B0=D1=87?= =?UTF-8?q?=D0=B0=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + robbery.js | 249 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 246 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0d48db5..682e732 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea /node_modules *.log +index.1.js diff --git a/robbery.js b/robbery.js index 4a8309d..6ca9728 100644 --- a/robbery.js +++ b/robbery.js @@ -6,6 +6,211 @@ */ exports.isStar = true; +function parseTime(timeString, containsDay) { + let daysOfWeek = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС']; + let day = 0; + if (containsDay) { + let dayName = timeString.match(/(..)/)[1]; + day = daysOfWeek.indexOf(dayName); + } + let numbers = timeString.match(/(\d+):(\d+)\+(\d+)$/); + let hours = Number.parseInt(numbers[1]); + let minutes = Number.parseInt(numbers[2]); + let timeZone = Number.parseInt(numbers[3]); + + return (24 * day + hours - timeZone) * 60 + minutes; +} + +function convertRobbersSchedule(schedule) { + let newSchedule = []; + let keys = ['Danny', 'Rusty', 'Linus']; + keys.forEach(key => { + schedule[key].forEach(interval => { + newSchedule.push({ + from: parseTime(interval.from, true), + to: parseTime(interval.to, true) + }); + }); + }); + + return newSchedule; +} + +function removeEmptyIntervals(schedule) { + return schedule.filter(interval => interval.from !== interval.to); +} + +function checkIntersection(a, b) { + return ((a.from <= b.to && a.from >= b.from) || + (a.to <= b.to && a.to >= b.from) || + (a.from < b.from && a.to > b.to)); +} + +function uniteIntersectedIntervals(a, b) { + return { + from: Math.min(a.from, b.from), + to: Math.max(a.to, b.to) + }; +} + +function addInterval(intervals, newInterval) { + let result = []; + intervals.forEach(interval => { + if (checkIntersection(interval, newInterval)) { + newInterval = uniteIntersectedIntervals(interval, newInterval); + } else { + result.push(interval); + } + }); + result.push(newInterval); + + return result; +} + +function removeScheduleIntersections(schedule) { + let unitedSchedule = []; + schedule.forEach(interval => { + unitedSchedule = addInterval(unitedSchedule, interval); + }); + + return unitedSchedule; +} + +function sortSchedule(schedule) { + return schedule.sort((a, b) => { + a = a.from; + b = b.from; + if (a < b) { + return -1; + } else if (a > b) { + return 1; + } + + return 0; + }); +} + +function convertBankSchedule(schedule) { + let from = parseTime(schedule.from, false); + let to = parseTime(schedule.to, false); + + return [0, 1, 2].map(day => { + return { + from: from + day * 24 * 60, + to: to + day * 24 * 60 + }; + }); +} + +function joinOnTimeline(schedule, bankSchedule) { + let timeline = []; + schedule.forEach(busyInterval => { + timeline.push({ + time: busyInterval.from, + type: 'busyStart' + }); + timeline.push({ + time: busyInterval.to, + type: 'busyEnd' + }); + }); + bankSchedule.forEach(bankInterval => { + timeline.push({ + time: bankInterval.from, + type: 'bankStart' + }); + timeline.push({ + time: bankInterval.to, + type: 'bankEnd' + }); + }); + timeline.sort((a, b) => { + if (a.time < b.time) { + return -1; + } else if (a.time > b.time) { + return 1; + } + let sortPriority = ['busyEnd', 'bankStart', 'busyStart', 'bankEnd']; + + return sortPriority.indexOf(a.type) - sortPriority.indexOf(b.type); + }); + + return timeline; +} + +function switchState(previous, type) { + switch (type) { + case 'bankStart': + previous.bank = true; + break; + case 'bankEnd': + previous.bank = false; + break; + case 'busyStart': + previous.busy = true; + break; + case 'busyEnd': + previous.busy = false; + break; + default: + break; + } +} + +function findRobberyIntervals(schedule, bankSchedule) { + let freeTime = []; + let timeline = joinOnTimeline(schedule, bankSchedule); + let addFreeTime = (from, to) => freeTime.push( + { + from: from, to: to + } + ); + timeline.reduce((previous, point) => { + if (previous === null) { + previous = { + start: null, + busy: false, + bank: false + }; + } + switchState(previous, point.type); + if (previous.start !== null) { + addFreeTime(previous.start, point.time); + previous.start = null; + } + if (!previous.busy && previous.bank) { + previous.start = point.time; + } + + return previous; + }, null); + + return freeTime; +} + +function findStartTimes(robberyIntervals, duration) { + let startTimes = []; + // let lastRobberyTime = 0; + // if (robberyIntervals.length > 0) { + // lastRobberyTime = robberyIntervals[0].from - 30; + // } + robberyIntervals.forEach(interval => { + // for (let time = Math.max(interval.from, lastRobberyTime + 30); + for (let time = interval.from; + time + duration <= interval.to; + time += 30) { + // lastRobberyTime = time; + startTimes.push(time); + } + }); + + return startTimes; +} + +function extractBankTimeZone(time) { + return Number.parseInt(time.match(/\+(\d+)$/)[1]); +} + /** * @param {Object} schedule – Расписание Банды * @param {Number} duration - Время на ограбление в минутах @@ -15,16 +220,26 @@ exports.isStar = true; * @returns {Object} */ exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + schedule = convertRobbersSchedule(schedule); + schedule = removeEmptyIntervals(schedule); + schedule = removeScheduleIntersections(schedule); + schedule = sortSchedule(schedule); + let bankSchedule = convertBankSchedule(workingHours); + let robberyIntervals = findRobberyIntervals(schedule, bankSchedule); + let startTimes = findStartTimes(robberyIntervals, duration); + let bankTimeZone = extractBankTimeZone(workingHours.from); return { + startTimes: startTimes, + currentPos: 0, + bankTimeZone: bankTimeZone, /** * Найдено ли время * @returns {Boolean} */ exists: function () { - return false; + return this.startTimes.length > 0; }, /** @@ -35,7 +250,28 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {String} */ format: function (template) { - return template; + if (this.startTimes.length === 0) { + return ''; + } + let daysOfWeek = ['ПН', 'ВТ', 'СР', 'ЧТ', 'ПТ', 'СБ', 'ВС']; + let time = this.startTimes[this.currentPos] + this.bankTimeZone * 60; + let day = daysOfWeek[Math.floor(time / (24 * 60))]; + time = time % (24 * 60); + let formatNumber = number => { + let text = number.toString(); + if (text.length === 1) { + text = '0'.concat(text); + } + + return text; + }; + let hours = formatNumber(Math.floor(time / 60)); + let minutes = formatNumber(time % 60); + + return template + .replace(/%DD/g, day) + .replace(/%HH/g, hours) + .replace(/%MM/g, minutes); }, /** @@ -44,7 +280,12 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ tryLater: function () { - return false; + if (this.currentPos === this.startTimes.length - 1 || this.startTimes.length === 0) { + return false; + } + this.currentPos++; + + return true; } }; }; From e5fcbdaf062240f17715fb0b74137ac7d38099d9 Mon Sep 17 00:00:00 2001 From: Denis Mukhametov Date: Thu, 19 Oct 2017 10:04:52 +0500 Subject: [PATCH 2/2] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81=20=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=B1=D0=BA=D0=B8,=20=D0=BA=D0=BE=D1=82=D0=BE=D1=80?= =?UTF-8?q?=D1=83=D1=8E=20=D0=BD=D0=B5=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87?= =?UTF-8?q?=D0=B0=D1=8E=D1=82=20=D1=82=D0=B5=D1=81=D1=82=D1=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- robbery.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/robbery.js b/robbery.js index 6ca9728..157b202 100644 --- a/robbery.js +++ b/robbery.js @@ -190,16 +190,16 @@ function findRobberyIntervals(schedule, bankSchedule) { function findStartTimes(robberyIntervals, duration) { let startTimes = []; - // let lastRobberyTime = 0; - // if (robberyIntervals.length > 0) { - // lastRobberyTime = robberyIntervals[0].from - 30; - // } + let lastRobberyTime = 0; + if (robberyIntervals.length > 0) { + lastRobberyTime = robberyIntervals[0].from - 30; + } robberyIntervals.forEach(interval => { - // for (let time = Math.max(interval.from, lastRobberyTime + 30); - for (let time = interval.from; + for (let time = Math.max(interval.from, lastRobberyTime + 30); + // for (let time = interval.from; time + duration <= interval.to; time += 30) { - // lastRobberyTime = time; + lastRobberyTime = time; startTimes.push(time); } });