// вспомогательная функция для нахождения всех элементов определенного типаconst findAll = (parent, selector) => parent.querySelectorAll(selector)const hide = (...els) => [...els].forEach(el => el.style.display = 'none')// примерhide(findAll(document, 'img')) // находим и скрываем все элементы "img" на странице
// вспомогательная функция для нахождения одного элемента определенного типаconst findOne = (parent, selector) => parent.querySelector(selector)const hasClass = (el, className) => el.classList.contains(className)// примерhasClass(findOne(document, 'p'), 'special') // true
const toggleClass = (el, className) => el.classList.toggleClass(className)// примерtoggleClass(findOne('p'), 'special') // параграф больше не имеет класса "special"
const getScrollPosition = (el = window) => ({ x: el.pageXOffset !== undefined ? el.pageXOffset : el.scrollLeft, y: el.pageYOffset !== undefined ? el.pageYOffset : el.scrollTop})// примерgetScrollPosition() // { x: 0, y: 200 }
const scrollToTop = () => { const c = document.documentElement.scrollTop || document.body.scrollTop if (c > 0) { requestAnimationFrame(scrollToTop) scrollTo(0, c - c / 8) }}// илиscrollTo({ top: 0, behavior: 'smooth'})// примерscrollToTop()
const elementContains = (parent, child) => parent !== child && parent.contains(child)// примерelementContains(findOne(document, 'head'), findOne(document, 'title')) // trueelementContains(findOne(document, 'body'), findOne(document, 'body')) // false
const elemIsVidibleInViewport = (el, partiallyVisible = false) => { const { top, left, bottom, right } = el.getBoundingClientRect() const { innerHeight, innerWidth } = window return partiallyVisible ? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) && ((left > 0 && left < innerWidth || right > 0 && right < innerWidth)) : top >= 0 & left >=0 && bottom <= innerHeight && rigth <= innerWidth}// примерelemIsVidibleInViewport(el) // элемент должен быть виден полностьюelemIsVidibleInViewport(el, true) // может быть виден частично
const getImages = (el, includeDuplicates = false) => { const images = [...el.getElementsByTagName('img')].map(img => img.getAttribute('src')) return includeDuplicates ? images : [...new Set(images)]}// примерgetImages(document, true) // получаем все изображения на страницеgetImages(document, false) // получаем только уникальные изображения
const detectDeviceType = () => /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ? 'Mobile' : 'Desktop'// ExampledetectDeviceType()
const getURLParams = url => (url.match(/([^?=&]+)(=([^&]*))/g) || []).reduce( (a, v) => ((a[v.slice(0, v.indexOf('='))] = v.slice(v.indexOf('=') + 1)), a), {})// примерgetURLParams('http://url.com/page?f=John&l=Smith') // { f: 'John', l: 'Smith' }getURLParams('https://google.com') // { }
const formToObject = form => Array.from(new FormData(form)).reduce( (acc, [key, value]) => ({ ...acc, [key]: value }), {} )// примерformToObject(findOne(document, 'form')) // { name: 'John', email: "myemail@example.com" }
const getProps = (from, ...selectors) => [...selectors].map(s => s .replace(/\[([^\[\]]*)\]/g, '.$1.') .split('.') .filter(t => t !== '') .reduce((prev, cur) => prev && prev[cur], from) )const obj = { selector: { to: { value: 'value to select' } }, target: [ 1, 2, { a: 'test' } ] }// примерgetProps(obj, 'selector.to.value', 'target[0]', 'target[2].a') // [ 'value to select', 1, 'test' ]
// вспомогательная функция для вывода сообщения в консольconst log = (value) => console.log(value)const delay = (fn, wait, ...rest) => setTimeout(fn, wait, ...rest)// примерdelay( text => log(text), 1000, 'позже')// 'позже' через 1 секунду
// вспомогательная функция для добавления обработчикаaddListener = (el, evt, cb) => el.addEventListener(evt, cb)const removeListener = (el, evt, fn, opts = false) => el.removeEventListener(evt, fn, opts)const fn = () => log('!')// пример;((B) => { addListener(B, 'click', fn) removeListener(B, 'click', fn) // сообщение "!" больше не выводится в консоль после клика})(document.body)
const formatDuration = ms => { if (ms < 0) ms = -ms const time = { // ~~ = Math.floor day: ~~(ms / 86400000), hour: ~~(ms / 3600000) % 24, minute: ~~(ms / 60000) % 60, second: ~~(ms / 1000) % 60, millisecond: ~~(ms) % 1000 } return Object.entries(time) .filter(val => val[1] !== 0) .map(([key, val]) => `${val} ${key}${val !== 1 ? 's' : ''}`) .join(', ')}// примерformatDuration(1001); // 1 second, 1 millisecondformatDuration(34325055574); // 397 days, 6 hours, 44 minutes, 15 seconds, 574 milliseconds
const getDaysDiffBetweenDates = (dateInitial, dateFinal) => (dateFinal - dateInitial) / (1000 * 3600 * 24)// примерlog( getDaysDiffBetweenDates(new Date('2020-11-01'), new Date('2020-11-09'))) // 8
// XMLHttpRequestconst httpGet = (url, cb, err = console.error) => { const req = new XMLHttpRequest() req.open('GET', url, true) req.onload = () => cb(req.responseText) req.onerror = () => err(req) req.send()}httpGet( 'https://jsonplaceholder.typicode.com/posts/1', log) // { "userId": 1, "id": 1, "title": "some title", "body": "some text" }// fetchconst httpGet = (url, cb, err = console.error) => fetch(url) .then(response => response.json()) .then(result => cb(result)) .catch(error => err(error))// async/awaitconst httpGet = async (url, cb, err = console.error) => { try { const response = await fetch(url); const result = await response.json(); cb(result); } catch (error) { err(error) }}
// XMLHttpsRequestconst httpPost = (url, data, cb, err = console.error) => { const req = new XMLHttpRequest() req.open('POST', url, true) req.setRequestHeader('Content-Type', 'application/json') req.onload = () => cb(req.responseText) req.onerror = () => err(req) req.send(data)}const newPost = { userId: 1234, title: 'foo', body: 'bar baz qux'}const data = JSON.stringify(newPost)httpPost( 'https://jsonplaceholder.typicode.com/posts', data, log) // { "id": 101, "userId": 1234, "title": "foo", "body": "bar baz qux" }// async/awaitconst httpPost = async (url, data, cb, err = console.error) => { try { const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) const result = await response.json() cb(result) } catch (error) { err(error) }}httpPost( 'https://jsonplaceholder.typicode.com/posts', newPost, log)
const counter = (selector, start, end, step = 1, duration = 2000) => { let current = start, _step = (end - start) * step < 0 ? -step : step, timer = setInterval(() => { current += _step findOne(document, selector).innerHTML = current if (current >= end) findOne(document, selector).innerHTML = end if (current >= end) clearInterval(timer) }, Math.abs(~~(duration / (end - start)))) return timer}// примерcounter('#some_id', 1, 1000, 5, 2000) // создаем двухсекундный таймер для элемента с идентификатором "some_id"
const copyToClipboard = str => { const el = document.createElement('textarea') el.value = str el.setAttribute('readonly') el.style.position = 'absolute' el.style.left = '-999px' document.body.append(el) const selected = document.getSelection().rangeCount > 0 ? document.getSelection().getRangeAt(0) : false el.select() document.execCommand('copy') el.remove() if (selected) { document.getSelection().removeAllRanges() document.getSelection().addRange(selected) }}// примерcopyToClipboard('some text') // строка "some text" скопирована в буфер обмена
Недавно разбирался в старых своих наработках/скриптах и наткнулся на скрипт где решалась задача о ферзях. Собственно это послужило написанию статьи о том как проходили этапы написания его алгоритма. Возможно пригодится начинающим программистам для решения похожих задач (код в примерах написан на java).
4 года назад была шумиха по поводу задачи о расположении 1000
ферзей на доске 1000х1000. Дело в том что расположение ферзей так
чтобы они не били
друг друга на доске является задачей
с большим количество итераций и как результат долгой по времени
выполнения. Как и многим мне захотелось проверить можно ли её
решить за приемлемое время.
На картинке расположено 8 ферзей которые не пересекаются по горизонтальной, вертикальной или диагональным линиям.
Надо написать скрипт который будет расставлять на доске ферзей по таким правилам.
Для поиска фигур была выбрана рекурсия.
Описание метода который вызывает сам себя:
Если переданная клетка пересекается с другими фигурами то
возвращаем false
Если переданная клетка не пересекается ни с кем то:
устанавливает флаг на доске в true
для этой
позиции.
Если мы дошли до конца (нету следующего ряда) то возвращаем
true
.
Находит первую клетку в следующем ряду и вызывает сам себя с новой клеткой.
Если вернулся false
то отправляем следующую клетку
из ряда или если не осталось клеток то возвращаем
false
) Предварительно ставим флаг в false
на доске у клетки которую изначально получили.
Если вернулся true
то возвращаем результат.
Такой метод для досок 8x8, 32x32, 50x50 отрабатывает хоть как то. Но если больше то уходит много времени.
На картинке можно заметить что количество свободных клеток у
рядов разное.
Если начинать поиск с ряда у которого меньше всего свободных клеток
то быстрее можно отсеивать тупиковые комбинации и выше вероятность
пройти до конца.
В скрипте добавил два списка с свободными колонками и
рядами.
Во время проверки из них генерируется список свободных клеток где
ряды отсортированы по возрастанию. Поиск начинается с самого
первого ряда.
Идея в том чтобы работать только с свободными клетками и начинать
поиск с самого маленького ряда.
После этого результат можно было получить вплоть до 400x400.
Есть множество комбинаций как расположить ферзей на доске.
Это и есть самая главная сложность в задаче.
Но в данном случае нужно найти лишь одну правильную комбинацию.
Обратите внимание на картинку.
Часть ферзей можно расположить заранее на доску в соответствии с
правилами.
Нужно лишь начать с второй клетки первого ряда и дальше добавлять
новые фигуры по формуле "row+1" и "column+2" Этот алгоритм заполнит
половину ферзей на доске, а дальше всё сделает скрипт оптимизации
который будет находить ряды с наименьшим числом клеток и
устанавливать там фигуры.
Поиск на доске 1000x1000 занял ~4 минуты (на процессоре: Intel Core i5-10400H CPU 2.60GHz).
Поиск на доске 1116x1116 занял ~6 минут.