Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import scala.io.Source
- //import scala.util.{ Try, Success, Failure }
- object Lysgo {
- case class English_LT_Word private (word: String)
- object English_LT_Word {
- def apply(word: String) = new English_LT_Word(word)
- def createNewWord(word: String): Either[String, English_LT_Word] = {
- if (word.length > 0 && word.forall(x=>consonants.contains(x) || vowels.contains(x))) Right(apply(word))
- else Left("'" + word + "'" + " is an invalid word")
- }
- }
- sealed trait Orientation
- object Orientation {
- case object HORIZONTAL extends Orientation
- case object VERTICAL extends Orientation
- }
- private val vowels = "AaąĄeEęĘėĖiIįĮyYoOuUųŲūŪ"
- private val consonants = "BbCcČčDdFfGgHhJjKkLlMmNnPpRrSsŠšTtVvZzŽžXxQqWw"
- sealed trait XO {
- def isVowel(letter: Char): Boolean = vowels.contains(letter.toLower)
- def isConsonant(letter: Char): Boolean = !isVowel(letter.toLower)
- def matches(char: Char): Boolean
- }
- case object X extends XO {
- override def matches(char: Char): Boolean = isConsonant(char)
- }
- case object O extends XO {
- override def matches(char: Char): Boolean = isVowel(char)
- }
- case class XOSymbol(data: XO, pos: Vec2)
- case class CrosswordXOData(data: Vector[XOSymbol])
- case class Vec2(x: Int, y: Int)
- case class XOWord(orientation: Orientation, word: Vector[XO], startPos: Vec2)
- case class ABCWord(orientation: Orientation, word: English_LT_Word, startPos: Vec2)
- case class CrosswordText private (lines: Vector[String])
- object CrosswordText {
- def apply(lines: Vector[String]) = new CrosswordText(lines)
- def createNewCrossWord(lines: Vector[String]): Either[String, CrosswordText] = {
- if (lines.flatten.forall(x=> consonants.contains(x) || vowels.contains(x) || x == ' ')) Right(apply(lines))
- else Left("Invalid crossword sentence error")
- }
- }
- case class Batch(id: Int, glyphs: Vector[Glyph], possibleABCWords: Vector[English_LT_Word], offset: Int)
- case class Glyph(orientation: Orientation, XOWord: Vector[XO], startPos: Vec2, batchID: Int)
- case class BatchCombination(wordID : Int, batchID: Int)
- case class Game (possibleWords : Vector[English_LT_Word], XO_Words: Vector[XOWord], crosswordXOData: CrosswordXOData, crosswordABCData: CrosswordText) {
- private val vowels = "aeiou"
- def isVowel(letter: Char): Boolean = vowels.contains(letter.toLower)
- def isConsonant(letter: Char): Boolean = !isVowel(letter)
- private def wordHasSamePattern(pattern : Vector[XO], word: English_LT_Word) : Boolean = {
- if(pattern.length == word.word.length && pattern.zipWithIndex.forall(xo => xo._1.matches(word.word(xo._2))))
- true
- else
- false
- }
- def printAllSolutions() : Either[String, Unit] = {
- val groupedGlyphs = XO_Words.groupBy(_.word).toVector
- val allGlyphs = (for(cbatch <- 0 until groupedGlyphs.length) yield {
- val currentGlyphs = for(g <- groupedGlyphs(cbatch)._2) yield {
- Glyph(g.orientation, g.word, g.startPos, cbatch)
- }
- currentGlyphs
- }).toVector //Gaunam visus XO zodzius sugrupuotus pagal XO pavadinimus, pvz.:
- //XX XX XX, OOOXXO OOOXXO, XO XO => (kur skirtingos pozicijos ar orientacijos kiekvienos grupes. Morint pamatyt tiesiog spausdint galima)
- val batches = for(g <- 0 until allGlyphs.length) yield { //Sukuriam batch'us visu vienodu XO zodziu ir sudedam galimus zodzius kurie ten patekt gali ABC
- val possibleAbc_Words = possibleWords.filter(word => wordHasSamePattern(allGlyphs(g)(0).XOWord, word))
- Batch(allGlyphs(g)(0).batchID, allGlyphs(g), possibleAbc_Words, allGlyphs.take(g).flatten.toVector.length)
- }
- val indexes_for_combination_generation = for (i <- batches) yield {
- /*Kiekvienas batch tures savo offseta, nes einant per batchus pvz.: i = 0,1,2,3,4,5,6,7
- o batchai eiti 012 01 01 0123 0143 tai turint praeitu batchu dydius mes galim apsiskaiciuoti offseta ir 'i' indeksa naudoti pozicijom gauti*/
- i.glyphs.length
- }
- val indexes = for(ofs <- indexes_for_combination_generation.zipWithIndex) yield {
- val indexes = for(id <- List.range(0, ofs._1)) yield {
- BatchCombination(id, ofs._2)
- }
- indexes
- }
- //Pereinami visi galimi solutions
- val NonDefaultCombinationalWords = for(i <- indexes.toList.flatten.toVector.permutations.toVector) yield {
- //Pereinant per vissu glyphus mes turime uztikrinti kad einamasis indeksas nera uz einamojo batcho ribu:
- val notFixedCombinationalWords = for(c <- i.zipWithIndex if(batches(i(c._2).batchID).offset <= c._2 && c._2 < indexes(c._1.batchID).length + batches(i(c._2).batchID).offset)) yield {
- //Graziname nauja solutiona
- val b = i(c._2)
- ABCWord(batches(b.batchID).glyphs(c._2 - batches(b.batchID).offset).orientation,
- batches(b.batchID).possibleABCWords(b.wordID),
- batches(b.batchID).glyphs(c._2 - batches(b.batchID).offset).startPos)
- }
- notFixedCombinationalWords
- }
- //ANSWER: Cia yra atfiltruoti teisingi variantai. Pries tai jau default surasem tai beliko visus likusius variantus cia surasyti:
- val cw = NonDefaultCombinationalWords.filter(x => x.length == indexes.flatten.toVector.length)
- for(i <- cw) {
- for {
- solution <- uploadWords(0, i, crosswordABCData)
- } yield validSolution(solution) match{
- case Right(s) => if(s) { solution.lines foreach println }
- case Left(sa) => return Left(sa)
- }
- }
- Right(())
- }
- def validSolution(solution: CrosswordText) : Either[String, Boolean] = {
- val verticalABCWords = getVerticalABCWords(solution)
- val horizontalABCWords = getHorizontalABCWords(solution)
- val vertical = verticalABCWords match{
- case Right(s) => for(i <- s) yield { i.word.word }
- case Left(s) => return Left(s)
- }
- val horizontal = horizontalABCWords match{
- case Right(s) => for(i <- s) yield { i.word.word }
- case Left(s) => return Left(s)
- }
- val both = vertical ++ horizontal
- Right(both.diff(possibleWords.map(_.word)).isEmpty)
- }
- def insertWord(myWord : ABCWord, txt : CrosswordText) : Either[String, CrosswordText] = {
- /*Paprasti algoritmai: Iskerpama nuo pradzios iki einamojo indekso, iklijuojamas reikiamas simbolis(-iai), iklijuojama eilutes pabaiga*/
- myWord.orientation match {
- case Orientation.VERTICAL =>
- val fixedL = for(i <- myWord.startPos.y to myWord.startPos.y + myWord.word.word.length-1) yield {
- txt.lines(i).substring(0, myWord.startPos.x) +
- myWord.word.word(i - myWord.startPos.y) +
- txt.lines(i).drop(myWord.startPos.x+1).take(txt.lines(i).length - (myWord.startPos.x+1))
- }
- val cCrossword = CrosswordText.createNewCrossWord(txt.lines.take(myWord.startPos.y) ++ fixedL ++ txt.lines.drop(myWord.startPos.y + myWord.word.word.length))
- cCrossword match {
- case Right(s) => Right(s)
- case Left(f) => Left(f)
- }
- //Verticaliai ir horizontaliai taip pat padarys:
- case Orientation.HORIZONTAL =>
- val modifiedLine = txt.lines(myWord.startPos.y).substring(0, myWord.startPos.x) + myWord.word.word + txt.lines(myWord.startPos.y).drop(myWord.startPos.x + myWord.word.word.length)
- val cCrossword = CrosswordText.createNewCrossWord(txt.lines.take(myWord.startPos.y) ++ Vector(modifiedLine) ++ txt.lines.drop(myWord.startPos.y + 1))
- cCrossword match {
- case Right(s) => Right(s)
- case Left(f) => Left(f)
- }
- }
- }
- //Irasysim zodzius su rekursija:
- def uploadWords(id: Int, words: Vector[ABCWord], txt: CrosswordText) : Either[String, CrosswordText] = {
- if(id == words.length)
- return Right(txt)
- val insertedWord = insertWord(words(id), txt)
- val t = insertedWord match {
- case Right(s) => uploadWords(id + 1, words, s)
- case Left(f) => Left(f)
- }
- t match {
- case Right(s) => Right(s)
- case Left(f) => Left(f)
- }
- }
- private def getHorizontalABCWords(crosswordData: CrosswordText) : Either[String, Vector[ABCWord]] = {
- val max = crosswordData.lines.map(_.size).max // BBBBBBB => 7
- val lines = crosswordData.lines.map(s => s.padTo(max, ' '))
- val horizontalWords = for(i <- 0 to lines.length - 1) yield
- {
- val pieces = lines(i).split("(?= )|(?<= )")
- val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
- val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
- val objWords = for(c <- tokensWithStartIndices if c._1 != " ") yield {
- val w = English_LT_Word.createNewWord(c._1)
- w match{
- case Right(success) => ABCWord(Orientation.HORIZONTAL, success, Vec2(c._2, i))
- case Left(fail) => return Left(fail)
- }
- }
- objWords.toVector //Array of words
- }.filter(x => x.word.word.length > 1)
- Right(horizontalWords.flatten.toVector)
- }
- private def getVerticalABCWords(crosswordData: CrosswordText) : Either[String, Vector[ABCWord]] = {
- val max = crosswordData.lines.map(_.size).max // BBBBBBB => 7
- val lines = crosswordData.lines.map(s => s.padTo(max, ' '))
- //Convert i list nes transpose naudoju(not sure are cia usseles)
- val verticalData = lines.toList.transpose.map(xs => xs.mkString) //Norint gati vertikalius zodzius transposinsim
- //Gautuose atsakymuose yra tusciu tarpu simboliai. Ju nefiltruojam pries imdami zodzius, nes jie yra koordinates kuriu mes negalime prarasti
- val verticalWords = for(i <- 0 to verticalData.length - 1) yield {
- //Kiekvienas vertikalus zodis yra vienas string tiesiog jis tures savo ORIENTATION vertikalu. Todel kai turim viename stringe vertikalius zodzius
- //Galim susirasti ju pozicijas stringe ir taip gausim koordinates
- val pieces = verticalData(i).split("(?= )|(?<= )")
- val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
- val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
- val objWords = for(c <- tokensWithStartIndices if c._1 != " ") yield {
- val w = English_LT_Word.createNewWord(c._1)
- w match{
- case Right(success) => ABCWord(Orientation.VERTICAL, success, Vec2(i, c._2))
- case Left(fail) => return Left(fail)
- }
- }
- objWords.toVector //Array of words
- }.filter(x => x.word.word != " " && x.word.word.length > 1)
- Right(verticalWords.flatten.toVector)
- }
- }
- object Game {
- //apply grazins visa reikiama informacija t.y. vertikalius ir horizontalius zodzius
- def apply(file: String): Either[String, Game] = {
- if(file.takeRight(4) != ".txt")
- Left("file isnt txt")
- val source = Source.fromFile(file)
- val fileData = source.getLines().toVector
- source.close
- val crossWordData = fileData.tail.tail //Pasiemam XO zodzius
- val possibleWords = for(cWord <- fileData.head.split(" ").toVector) yield {
- English_LT_Word.createNewWord(cWord) match {
- case Right(s) => s
- case Left(f) => return Left(f)
- }
- }
- val mGame = for {
- crosswordData <- getCrosswordData(crossWordData)
- crosswordXODataABC <- crosswordXOData_TO_Abc(CrosswordXOData(crosswordData.data))
- words <- getWords(CrosswordXOData(crosswordData.data))
- } yield Game(possibleWords, words, CrosswordXOData(crosswordData.data), crosswordXODataABC)
- mGame match {
- case Right(s) => {
- if(s.possibleWords.length != s.XO_Words.length)
- Left("XO ir ABC zodziu kiekiai nesutampa")
- else
- Right(s)
- }
- case Left(f) => Left(f)
- }
- }
- private def getCrosswordData(fileData: Vector[String]) : Either[String, CrosswordXOData] = {
- //Cia bus y koordinate
- val convertedText = for(i <- 0 to fileData.length - 1) yield {
- //Paemu einamosios eilutes zodzius su ju pradzius indeksais(koordinate x gaunu cia)
- val pieces = fileData(i).split("(?= )|(?<= )")
- val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
- val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
- //Invalid data ivesta i kryziazodi
- if(fileData(i).filter(x => x != 'X' && x != 'O' && x != ' ').length > 0) //Neteisinga info ideta
- {
- return Left("ERROR - Input is not in a correct format")
- }
- //Jeigu simboli radom kazkuri - grazinam ta simboli, jeigu ten tarpas - pasizymim NONE
- val currentlyConvertedLine = for (s <- 0 to fileData(i).length - 1 if fileData(i)(s) != ' ') yield {
- val cMatch = fileData(i)(s) match {
- case 'X' => XOSymbol(X, Vec2(s, i))
- case 'O' => XOSymbol(O, Vec2(s, i))
- }
- cMatch
- }
- currentlyConvertedLine.toVector
- }
- return Right(CrosswordXOData(convertedText.flatten.toVector))
- }
- private def crosswordXOData_TO_Abc(crosswordXOData: CrosswordXOData) : Either[String, CrosswordText] = {
- val myMap = (for(i <- crosswordXOData.data) yield { i.pos -> i.data }).toMap
- val xGridLength = myMap.keysIterator.maxBy(e => e.y).y + 1
- val yGridLength = myMap.keysIterator.maxBy(e => e.x).x + 1
- val cgrid = for(x <- 0 to xGridLength - 1) yield {
- val currentLine = for(y <- 0 to yGridLength - 1) yield {
- myMap.keySet.exists(_ == Vec2(y,x)) match{
- case true => myMap(Vec2(y,x))
- case false => " "
- }
- }
- currentLine.mkString("")
- }
- val txt = CrosswordText.createNewCrossWord(cgrid.toVector)
- txt match {
- case Right(s) => Right(s)
- case Left(f) => Left(f)
- }
- }
- private def getWords(crosswordData: CrosswordXOData) : Either[String, Vector[XOWord]] = {
- val myGrid = crosswordXOData_TO_Abc(crosswordData)
- myGrid match {
- case Right(newGrid) =>
- val grid = newGrid.lines.toVector
- //Convert i list nes transpose naudoju(not sure are cia usseles)
- val verticalData = grid.toVector.transpose.map(xs => xs.mkString) //Norint gati vertikalius zodzius transposinsim
- //Gautuose atsakymuose yra tusciu tarpu simboliai. Ju nefiltruojam pries imdami zodzius, nes jie yra koordinates kuriu mes negalime prarasti
- val verticalWords = for(i <- 0 to verticalData.length - 1) yield {
- //Kiekvienas vertikalus zodis yra vienas string tiesiog jis tures savo ORIENTATION vertikalu. Todel kai turim viename stringe vertikalius zodzius
- //Galim susirasti ju pozicijas stringe ir taip gausim koordinates
- val pieces = verticalData(i).split("(?= )|(?<= )")
- val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
- val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
- val objWords = for(c <- tokensWithStartIndices) yield {
- val cXOWord = for(xo <- 0 to c._1.length - 1 if c._1(xo) != ' ') yield {
- c._1(xo) match {
- case 'X' => X
- case 'O' => O
- }
- }
- XOWord(Orientation.VERTICAL, cXOWord.toVector, Vec2(i, c._2))
- }
- objWords.toVector //Array of words
- }.filter(x => x.word != " " && x.word.length > 1)
- val horizontalWords = for(i <- 0 to grid.toVector.length - 1) yield
- {
- val pieces = grid(i).split("(?= )|(?<= )")
- val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
- val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
- val objWords = for(c <- tokensWithStartIndices) yield {
- val cXOWord = for(xo <- 0 to c._1.length - 1 if c._1(xo) != ' ') yield {
- c._1(xo) match {
- case 'X' => X
- case 'O' => O
- }
- }
- XOWord(Orientation.HORIZONTAL, cXOWord.toVector, Vec2(c._2, i))
- }
- objWords.toVector //Array of words
- }.filter(x => x.word.length > 1)
- Right(verticalWords.flatten.toVector ++ horizontalWords.flatten.toVector)
- case Left(fail) => Left(fail)
- }
- }
- }
- /*case class EnglishWord private(text: String) extends AnyVal
- object EnglishWord {
- def apply(text: String): Try[EnglishWord] = {
- if (isValid(text)) {
- Success(new EnglishWord(text))
- } else {
- Failure(new IllegalArgumentException("Invalid word: " + text))
- }
- }
- def isValid(s: String): Boolean = ???
- }*/
- def main(args: Array[String]): Unit =
- {
- val myGame = Game("Duomenys.txt")
- myGame match {
- case Right(validGame) => validGame.printAllSolutions()
- case Left(errorLog) => println(errorLog)
- }
- }
- }
Add Comment
Please, Sign In to add comment