diff --git a/robbery.js b/robbery.js index 4a8309d..f074e3c 100644 --- a/robbery.js +++ b/robbery.js @@ -6,6 +6,10 @@ */ exports.isStar = true; +var MINUTES_IN_HOUR = 60; +var HOURS_IN_DAY = 24; +var MINUTES_IN_DAY = MINUTES_IN_HOUR * HOURS_IN_DAY; + /** * @param {Object} schedule – Расписание Банды * @param {Number} duration - Время на ограбление в минутах @@ -15,7 +19,30 @@ exports.isStar = true; * @returns {Object} */ exports.getAppropriateMoment = function (schedule, duration, workingHours) { - console.info(schedule, duration, workingHours); + + if (!isWorkingHours(workingHours) || (!duration)) { + return { + exists: function () { + return false; + }, + format: function () { + return ''; + }, + tryLater: function () { + return false; + } + }; + } + + var bankUTC = getBankUTC(workingHours.from); + var bankIntervals = getBankIntervals(workingHours); + var gangIntervals = getGangIntervals(schedule, bankUTC); + + var robberyIntervals = findRobberyIntervals(bankIntervals, gangIntervals); + var goodTimes = getGoodTimesForRobbery(robberyIntervals, duration); + + var isExistsTime = goodTimes.length !== 0; + var robberyTime = goodTimes.shift(); return { @@ -24,7 +51,7 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ exists: function () { - return false; + return isExistsTime; }, /** @@ -35,7 +62,21 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {String} */ format: function (template) { - return template; + if (!isExistsTime || !template) { + return ''; + } + + var day = ['ПН', 'ВТ', 'СР'][Math.floor(robberyTime / MINUTES_IN_DAY)]; + + var hour = Math.floor((robberyTime % MINUTES_IN_DAY) / MINUTES_IN_HOUR); + hour = (hour < 10 ? '0' : '') + hour; + + var minute = (robberyTime % MINUTES_IN_DAY) % MINUTES_IN_HOUR; + minute = (minute < 10 ? '0' : '') + minute; + + return template.replace('%DD', day) + .replace('%HH', hour) + .replace('%MM', minute); }, /** @@ -44,7 +85,286 @@ exports.getAppropriateMoment = function (schedule, duration, workingHours) { * @returns {Boolean} */ tryLater: function () { - return false; + var oldTime = robberyTime; + robberyTime = goodTimes.shift() || robberyTime; + + return robberyTime !== oldTime; } }; }; + +function isWorkingHours(hours) { + return (hours) && (Object.keys(hours).length !== 0); +} + +function getBankUTC(dateAsString) { + return parseInt(dateAsString.split('+')[1]); +} + +function getBankIntervals(workingHours) { + var bankIntervals = []; + + var bankStart = transformStringToDate(workingHours.from); + var bankEnd = transformStringToDate(workingHours.to); + + bankIntervals.push( + { + from: convertDateToMinutes(bankStart, 'ПН'), + to: convertDateToMinutes(bankEnd, 'ПН') + }, + { + from: convertDateToMinutes(bankStart, 'ВТ'), + to: convertDateToMinutes(bankEnd, 'ВТ') + }, + { + from: convertDateToMinutes(bankStart, 'СР'), + to: convertDateToMinutes(bankEnd, 'СР') + } + ); + + return bankIntervals; +} + +function transformStringToDate(dateAsString) { + var dateComponents = dateAsString.split(/\s|:|\+/); + + if (dateComponents.length === 3) { + return { + day: '', + hour: parseInt(dateComponents[0]), + minute: parseInt(dateComponents[1]), + utc: parseInt(dateComponents[2]) + }; + } + + return { + day: dateComponents[0], + hour: parseInt(dateComponents[1]), + minute: parseInt(dateComponents[2]), + utc: parseInt(dateComponents[3]) + }; +} + +function convertDateToMinutes(date, day) { + if (day) { + date.day = day; + } + + return getMinutes(date); +} + +function getMinutes(date) { + var minutes = date.minute + date.hour * MINUTES_IN_HOUR; + + if (date.day === 'ПН') { + minutes += 0; + } else if (date.day === 'ВТ') { + minutes += MINUTES_IN_DAY; + } else if (date.day === 'СР') { + minutes += 2 * MINUTES_IN_DAY; + } else { + minutes += 3 * MINUTES_IN_DAY; + } + + return minutes; +} + +function getGangIntervals(shedule, bankUTC) { + var gangIntervals = []; + + if (!shedule || Object.keys(shedule).length === 0) { + return gangIntervals; + } + + var bandits = Object.keys(shedule); + + for (var i = 0; i < bandits.length; i++) { + var bandit = bandits[i]; + gangIntervals = gangIntervals.concat(getBanditShedule(shedule[bandit], bankUTC)); + } + + gangIntervals.sort(function (interval1, interval2) { + return interval1.from > interval2.from; + }); + + return gangIntervals; +} + +function getBanditShedule(banditShedule, bankUTC) { + var banditIntervals = []; + for (var i = 0; i < banditShedule.length; i++) { + banditIntervals.push(getBanditInterval(banditShedule[i], bankUTC)); + } + + return banditIntervals; +} + +function getBanditInterval(elementOfShedule, bankUTC) { + var dateStart = transformStringToDate(elementOfShedule.from); + var dateEnd = transformStringToDate(elementOfShedule.to); + + var minutesStart = convertDateToMinutes(dateStart); + var minutesEnd = convertDateToMinutes(dateEnd); + + var minutesStartWithUTC = addUTCToMinutes(minutesStart, dateStart.utc, bankUTC); + var minutesEndWithUTC = addUTCToMinutes(minutesEnd, dateEnd.utc, bankUTC); + + return { + from: minutesStartWithUTC, + to: minutesEndWithUTC + }; +} + +function addUTCToMinutes(minutes, banditUTC, bankUTC) { + return minutes - (banditUTC - bankUTC) * MINUTES_IN_HOUR; +} +// function findNewStart(gangIntervals, start) { + // return gangIntervals.filter(function (interval) { + // return interval.from <= start && start <= interval.to; + // }); +// } + +function findBadStart(gangIntervals, start, end) { + return gangIntervals.filter(function (interval) { + return start < interval.from && interval.to < end; + }); +} + +function exploreInterval(gangIntervals, robberyIntervals, robberyStart, robberyEnd) { + while (robberyStart <= robberyEnd) { + var badStart = findBadStart(gangIntervals, robberyStart, robberyEnd)[0]; + + var from = badStart !== undefined ? badStart.from : robberyEnd; + + robberyIntervals.push({ + from: robberyStart, + to: from + }); + + if (from === robberyEnd) { + break; + } + + robberyStart = badStart !== undefined ? badStart.to : robberyEnd; + + // robberyStart = findNewStart(gangIntervals, robberyStart) + // .map(function (elem) { + // return elem.to; + // }) + // .sort(function (a, b) { + // return b > a; + // })[0]; + robberyStart = findRobberyStart(gangIntervals, { from: robberyStart, to: robberyEnd }); + } +} + +function findRobberyIntervals(bankIntervals, gangIntervals) { + if (gangIntervals.length === 0) { + return bankIntervals; + } + + var robberyIntervals = []; + + for (var i = 0; i < bankIntervals.length; i++) { + var bankInterval = bankIntervals[i]; + // var robberyInterval = findPossibleRobberyInterval(gangIntervals, bankInterval); + var robberyStart = findRobberyStart(gangIntervals, bankInterval); + var robberyEnd = findRobberyEnd(gangIntervals, bankInterval); + if (robberyStart >= bankInterval.to || robberyEnd <= robberyStart) { + continue; + } + + exploreInterval(gangIntervals, robberyIntervals, robberyStart, robberyEnd); + } + + return robberyIntervals; +} + +function findRobberyStart(gangIntervals, bankInterval) { + var robberyStart = bankInterval.from; + + var newRobberyStart = gangIntervals.filter(function (interval) { + return interval.from <= bankInterval.from && bankInterval.from < interval.to; + }) + .map(function (intervals) { + return intervals.to; + }) + .sort(function (a, b) { + return a < b; + })[0]; + + if (newRobberyStart !== undefined && newRobberyStart !== robberyStart) { + var newInterval = { + from: newRobberyStart, + to: bankInterval.to + }; + robberyStart = findRobberyStart(gangIntervals, newInterval); + } + + return robberyStart; +} + +function findRobberyEnd(gangIntervals, bankInterval) { + var robberyEnd = bankInterval.to; + + var newRobberyEnd = gangIntervals.filter(function (interval) { + return interval.from < bankInterval.to && bankInterval.to <= interval.to; + }) + .map(function (intervals) { + return intervals.from; + }) + .sort(function (a, b) { + return a > b; + })[0]; + + if (newRobberyEnd !== undefined && newRobberyEnd !== robberyEnd) { + var newInterval = { + from: bankInterval.from, + to: newRobberyEnd + }; + robberyEnd = findRobberyEnd(gangIntervals, newInterval); + } + + return robberyEnd; +} + +// function findPossibleRobberyInterval(gangIntervals, bankInterval) { + // var robberyStart = gangIntervals.filter(function (interval) { + // return interval.from <= bankInterval.from && bankInterval.from < interval.to; + // }) + // .map(function (intervals) { + // return intervals.to; + // }) + // .sort(function (a, b) { + // return a < b; + // })[0]; + + // var robberyEnd = gangIntervals.filter(function (interval) { + // return interval.from < bankInterval.to && bankInterval.to <= interval.to; + // }) + // .map(function (intervals) { + // return intervals.from; + // }) + // .sort(function (a, b) { + // return a > b; + // })[0]; + + // robberyStart = robberyStart ? robberyStart : bankInterval.from; + // robberyEnd = robberyEnd ? robberyEnd : bankInterval.to; + + // return [robberyStart, robberyEnd]; +// } + +function getGoodTimesForRobbery(robberyIntervals, duration) { + var goodTimes = []; + var stepTime = 30; + + robberyIntervals.forEach(function (time) { + while (time.from + duration <= time.to) { + goodTimes.push(time.from); + time.from += stepTime; + } + }); + + return goodTimes; +}