Guest User

Untitled

a guest
Jul 15th, 2018
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.70 KB | None | 0 0
  1. import scala.io.Source
  2. //import scala.util.{ Try, Success, Failure }
  3.  
  4. object Lysgo {
  5.  
  6. case class English_LT_Word private (word: String)
  7. object English_LT_Word {
  8. def apply(word: String) = new English_LT_Word(word)
  9. def createNewWord(word: String): Either[String, English_LT_Word] = {
  10. if (word.length > 0 && word.forall(x=>consonants.contains(x) || vowels.contains(x))) Right(apply(word))
  11. else Left("'" + word + "'" + " is an invalid word")
  12. }
  13. }
  14.  
  15. sealed trait Orientation
  16. object Orientation {
  17. case object HORIZONTAL extends Orientation
  18. case object VERTICAL extends Orientation
  19. }
  20.  
  21. private val vowels = "AaąĄeEęĘėĖiIįĮyYoOuUųŲūŪ"
  22. private val consonants = "BbCcČčDdFfGgHhJjKkLlMmNnPpRrSsŠšTtVvZzŽžXxQqWw"
  23. sealed trait XO {
  24. def isVowel(letter: Char): Boolean = vowels.contains(letter.toLower)
  25. def isConsonant(letter: Char): Boolean = !isVowel(letter.toLower)
  26.  
  27. def matches(char: Char): Boolean
  28. }
  29.  
  30. case object X extends XO {
  31. override def matches(char: Char): Boolean = isConsonant(char)
  32. }
  33. case object O extends XO {
  34. override def matches(char: Char): Boolean = isVowel(char)
  35. }
  36.  
  37. case class XOSymbol(data: XO, pos: Vec2)
  38. case class CrosswordXOData(data: Vector[XOSymbol])
  39.  
  40. case class Vec2(x: Int, y: Int)
  41. case class XOWord(orientation: Orientation, word: Vector[XO], startPos: Vec2)
  42.  
  43. case class ABCWord(orientation: Orientation, word: English_LT_Word, startPos: Vec2)
  44. case class CrosswordText private (lines: Vector[String])
  45. object CrosswordText {
  46. def apply(lines: Vector[String]) = new CrosswordText(lines)
  47. def createNewCrossWord(lines: Vector[String]): Either[String, CrosswordText] = {
  48. if (lines.flatten.forall(x=> consonants.contains(x) || vowels.contains(x) || x == ' ')) Right(apply(lines))
  49. else Left("Invalid crossword sentence error")
  50. }
  51. }
  52.  
  53. case class Batch(id: Int, glyphs: Vector[Glyph], possibleABCWords: Vector[English_LT_Word], offset: Int)
  54. case class Glyph(orientation: Orientation, XOWord: Vector[XO], startPos: Vec2, batchID: Int)
  55. case class BatchCombination(wordID : Int, batchID: Int)
  56.  
  57.  
  58. case class Game (possibleWords : Vector[English_LT_Word], XO_Words: Vector[XOWord], crosswordXOData: CrosswordXOData, crosswordABCData: CrosswordText) {
  59. private val vowels = "aeiou"
  60.  
  61. def isVowel(letter: Char): Boolean = vowels.contains(letter.toLower)
  62. def isConsonant(letter: Char): Boolean = !isVowel(letter)
  63.  
  64. private def wordHasSamePattern(pattern : Vector[XO], word: English_LT_Word) : Boolean = {
  65. if(pattern.length == word.word.length && pattern.zipWithIndex.forall(xo => xo._1.matches(word.word(xo._2))))
  66. true
  67. else
  68. false
  69. }
  70.  
  71. def printAllSolutions() : Either[String, Unit] = {
  72.  
  73. val groupedGlyphs = XO_Words.groupBy(_.word).toVector
  74. val allGlyphs = (for(cbatch <- 0 until groupedGlyphs.length) yield {
  75. val currentGlyphs = for(g <- groupedGlyphs(cbatch)._2) yield {
  76. Glyph(g.orientation, g.word, g.startPos, cbatch)
  77. }
  78.  
  79. currentGlyphs
  80. }).toVector //Gaunam visus XO zodzius sugrupuotus pagal XO pavadinimus, pvz.:
  81. //XX XX XX, OOOXXO OOOXXO, XO XO => (kur skirtingos pozicijos ar orientacijos kiekvienos grupes. Morint pamatyt tiesiog spausdint galima)
  82.  
  83. 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
  84. val possibleAbc_Words = possibleWords.filter(word => wordHasSamePattern(allGlyphs(g)(0).XOWord, word))
  85. Batch(allGlyphs(g)(0).batchID, allGlyphs(g), possibleAbc_Words, allGlyphs.take(g).flatten.toVector.length)
  86. }
  87.  
  88. val indexes_for_combination_generation = for (i <- batches) yield {
  89. /*Kiekvienas batch tures savo offseta, nes einant per batchus pvz.: i = 0,1,2,3,4,5,6,7
  90. o batchai eiti 012 01 01 0123 0143 tai turint praeitu batchu dydius mes galim apsiskaiciuoti offseta ir 'i' indeksa naudoti pozicijom gauti*/
  91. i.glyphs.length
  92. }
  93.  
  94. val indexes = for(ofs <- indexes_for_combination_generation.zipWithIndex) yield {
  95. val indexes = for(id <- List.range(0, ofs._1)) yield {
  96. BatchCombination(id, ofs._2)
  97. }
  98. indexes
  99. }
  100.  
  101. //Pereinami visi galimi solutions
  102. val NonDefaultCombinationalWords = for(i <- indexes.toList.flatten.toVector.permutations.toVector) yield {
  103. //Pereinant per vissu glyphus mes turime uztikrinti kad einamasis indeksas nera uz einamojo batcho ribu:
  104. 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 {
  105. //Graziname nauja solutiona
  106. val b = i(c._2)
  107. ABCWord(batches(b.batchID).glyphs(c._2 - batches(b.batchID).offset).orientation,
  108. batches(b.batchID).possibleABCWords(b.wordID),
  109. batches(b.batchID).glyphs(c._2 - batches(b.batchID).offset).startPos)
  110. }
  111. notFixedCombinationalWords
  112. }
  113.  
  114. //ANSWER: Cia yra atfiltruoti teisingi variantai. Pries tai jau default surasem tai beliko visus likusius variantus cia surasyti:
  115. val cw = NonDefaultCombinationalWords.filter(x => x.length == indexes.flatten.toVector.length)
  116. for(i <- cw) {
  117. for {
  118. solution <- uploadWords(0, i, crosswordABCData)
  119. } yield validSolution(solution) match{
  120. case Right(s) => if(s) { solution.lines foreach println }
  121. case Left(sa) => return Left(sa)
  122. }
  123. }
  124.  
  125. Right(())
  126. }
  127.  
  128. def validSolution(solution: CrosswordText) : Either[String, Boolean] = {
  129.  
  130. val verticalABCWords = getVerticalABCWords(solution)
  131. val horizontalABCWords = getHorizontalABCWords(solution)
  132.  
  133. val vertical = verticalABCWords match{
  134. case Right(s) => for(i <- s) yield { i.word.word }
  135. case Left(s) => return Left(s)
  136. }
  137. val horizontal = horizontalABCWords match{
  138. case Right(s) => for(i <- s) yield { i.word.word }
  139. case Left(s) => return Left(s)
  140. }
  141.  
  142. val both = vertical ++ horizontal
  143.  
  144. Right(both.diff(possibleWords.map(_.word)).isEmpty)
  145. }
  146.  
  147. def insertWord(myWord : ABCWord, txt : CrosswordText) : Either[String, CrosswordText] = {
  148.  
  149. /*Paprasti algoritmai: Iskerpama nuo pradzios iki einamojo indekso, iklijuojamas reikiamas simbolis(-iai), iklijuojama eilutes pabaiga*/
  150. myWord.orientation match {
  151. case Orientation.VERTICAL =>
  152. val fixedL = for(i <- myWord.startPos.y to myWord.startPos.y + myWord.word.word.length-1) yield {
  153. txt.lines(i).substring(0, myWord.startPos.x) +
  154. myWord.word.word(i - myWord.startPos.y) +
  155. txt.lines(i).drop(myWord.startPos.x+1).take(txt.lines(i).length - (myWord.startPos.x+1))
  156. }
  157.  
  158. val cCrossword = CrosswordText.createNewCrossWord(txt.lines.take(myWord.startPos.y) ++ fixedL ++ txt.lines.drop(myWord.startPos.y + myWord.word.word.length))
  159. cCrossword match {
  160. case Right(s) => Right(s)
  161. case Left(f) => Left(f)
  162. }
  163.  
  164. //Verticaliai ir horizontaliai taip pat padarys:
  165. case Orientation.HORIZONTAL =>
  166. 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)
  167.  
  168.  
  169. val cCrossword = CrosswordText.createNewCrossWord(txt.lines.take(myWord.startPos.y) ++ Vector(modifiedLine) ++ txt.lines.drop(myWord.startPos.y + 1))
  170. cCrossword match {
  171. case Right(s) => Right(s)
  172. case Left(f) => Left(f)
  173. }
  174. }
  175. }
  176.  
  177. //Irasysim zodzius su rekursija:
  178. def uploadWords(id: Int, words: Vector[ABCWord], txt: CrosswordText) : Either[String, CrosswordText] = {
  179.  
  180. if(id == words.length)
  181. return Right(txt)
  182.  
  183. val insertedWord = insertWord(words(id), txt)
  184. val t = insertedWord match {
  185. case Right(s) => uploadWords(id + 1, words, s)
  186. case Left(f) => Left(f)
  187. }
  188.  
  189. t match {
  190. case Right(s) => Right(s)
  191. case Left(f) => Left(f)
  192. }
  193. }
  194.  
  195. private def getHorizontalABCWords(crosswordData: CrosswordText) : Either[String, Vector[ABCWord]] = {
  196.  
  197. val max = crosswordData.lines.map(_.size).max // BBBBBBB => 7
  198. val lines = crosswordData.lines.map(s => s.padTo(max, ' '))
  199.  
  200. val horizontalWords = for(i <- 0 to lines.length - 1) yield
  201. {
  202. val pieces = lines(i).split("(?= )|(?<= )")
  203. val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
  204. val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
  205.  
  206. val objWords = for(c <- tokensWithStartIndices if c._1 != " ") yield {
  207. val w = English_LT_Word.createNewWord(c._1)
  208. w match{
  209. case Right(success) => ABCWord(Orientation.HORIZONTAL, success, Vec2(c._2, i))
  210. case Left(fail) => return Left(fail)
  211. }
  212. }
  213.  
  214. objWords.toVector //Array of words
  215. }.filter(x => x.word.word.length > 1)
  216.  
  217. Right(horizontalWords.flatten.toVector)
  218. }
  219.  
  220. private def getVerticalABCWords(crosswordData: CrosswordText) : Either[String, Vector[ABCWord]] = {
  221. val max = crosswordData.lines.map(_.size).max // BBBBBBB => 7
  222. val lines = crosswordData.lines.map(s => s.padTo(max, ' '))
  223.  
  224. //Convert i list nes transpose naudoju(not sure are cia usseles)
  225. val verticalData = lines.toList.transpose.map(xs => xs.mkString) //Norint gati vertikalius zodzius transposinsim
  226.  
  227. //Gautuose atsakymuose yra tusciu tarpu simboliai. Ju nefiltruojam pries imdami zodzius, nes jie yra koordinates kuriu mes negalime prarasti
  228. val verticalWords = for(i <- 0 to verticalData.length - 1) yield {
  229.  
  230. //Kiekvienas vertikalus zodis yra vienas string tiesiog jis tures savo ORIENTATION vertikalu. Todel kai turim viename stringe vertikalius zodzius
  231. //Galim susirasti ju pozicijas stringe ir taip gausim koordinates
  232. val pieces = verticalData(i).split("(?= )|(?<= )")
  233. val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
  234. val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
  235.  
  236. val objWords = for(c <- tokensWithStartIndices if c._1 != " ") yield {
  237. val w = English_LT_Word.createNewWord(c._1)
  238. w match{
  239. case Right(success) => ABCWord(Orientation.VERTICAL, success, Vec2(i, c._2))
  240. case Left(fail) => return Left(fail)
  241. }
  242. }
  243.  
  244. objWords.toVector //Array of words
  245. }.filter(x => x.word.word != " " && x.word.word.length > 1)
  246.  
  247. Right(verticalWords.flatten.toVector)
  248. }
  249. }
  250.  
  251. object Game {
  252. //apply grazins visa reikiama informacija t.y. vertikalius ir horizontalius zodzius
  253. def apply(file: String): Either[String, Game] = {
  254. if(file.takeRight(4) != ".txt")
  255. Left("file isnt txt")
  256.  
  257. val source = Source.fromFile(file)
  258. val fileData = source.getLines().toVector
  259. source.close
  260.  
  261. val crossWordData = fileData.tail.tail //Pasiemam XO zodzius
  262. val possibleWords = for(cWord <- fileData.head.split(" ").toVector) yield {
  263. English_LT_Word.createNewWord(cWord) match {
  264. case Right(s) => s
  265. case Left(f) => return Left(f)
  266. }
  267. }
  268.  
  269. val mGame = for {
  270. crosswordData <- getCrosswordData(crossWordData)
  271. crosswordXODataABC <- crosswordXOData_TO_Abc(CrosswordXOData(crosswordData.data))
  272. words <- getWords(CrosswordXOData(crosswordData.data))
  273.  
  274. } yield Game(possibleWords, words, CrosswordXOData(crosswordData.data), crosswordXODataABC)
  275.  
  276. mGame match {
  277. case Right(s) => {
  278. if(s.possibleWords.length != s.XO_Words.length)
  279. Left("XO ir ABC zodziu kiekiai nesutampa")
  280. else
  281. Right(s)
  282. }
  283. case Left(f) => Left(f)
  284. }
  285. }
  286.  
  287. private def getCrosswordData(fileData: Vector[String]) : Either[String, CrosswordXOData] = {
  288.  
  289. //Cia bus y koordinate
  290. val convertedText = for(i <- 0 to fileData.length - 1) yield {
  291.  
  292. //Paemu einamosios eilutes zodzius su ju pradzius indeksais(koordinate x gaunu cia)
  293. val pieces = fileData(i).split("(?= )|(?<= )")
  294. val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
  295. val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
  296.  
  297. //Invalid data ivesta i kryziazodi
  298. if(fileData(i).filter(x => x != 'X' && x != 'O' && x != ' ').length > 0) //Neteisinga info ideta
  299. {
  300. return Left("ERROR - Input is not in a correct format")
  301. }
  302.  
  303. //Jeigu simboli radom kazkuri - grazinam ta simboli, jeigu ten tarpas - pasizymim NONE
  304. val currentlyConvertedLine = for (s <- 0 to fileData(i).length - 1 if fileData(i)(s) != ' ') yield {
  305. val cMatch = fileData(i)(s) match {
  306. case 'X' => XOSymbol(X, Vec2(s, i))
  307. case 'O' => XOSymbol(O, Vec2(s, i))
  308. }
  309. cMatch
  310. }
  311.  
  312. currentlyConvertedLine.toVector
  313. }
  314.  
  315. return Right(CrosswordXOData(convertedText.flatten.toVector))
  316. }
  317.  
  318. private def crosswordXOData_TO_Abc(crosswordXOData: CrosswordXOData) : Either[String, CrosswordText] = {
  319. val myMap = (for(i <- crosswordXOData.data) yield { i.pos -> i.data }).toMap
  320.  
  321. val xGridLength = myMap.keysIterator.maxBy(e => e.y).y + 1
  322. val yGridLength = myMap.keysIterator.maxBy(e => e.x).x + 1
  323.  
  324. val cgrid = for(x <- 0 to xGridLength - 1) yield {
  325. val currentLine = for(y <- 0 to yGridLength - 1) yield {
  326. myMap.keySet.exists(_ == Vec2(y,x)) match{
  327. case true => myMap(Vec2(y,x))
  328. case false => " "
  329. }
  330. }
  331. currentLine.mkString("")
  332. }
  333.  
  334. val txt = CrosswordText.createNewCrossWord(cgrid.toVector)
  335. txt match {
  336. case Right(s) => Right(s)
  337. case Left(f) => Left(f)
  338. }
  339. }
  340.  
  341. private def getWords(crosswordData: CrosswordXOData) : Either[String, Vector[XOWord]] = {
  342.  
  343. val myGrid = crosswordXOData_TO_Abc(crosswordData)
  344. myGrid match {
  345. case Right(newGrid) =>
  346. val grid = newGrid.lines.toVector
  347. //Convert i list nes transpose naudoju(not sure are cia usseles)
  348. val verticalData = grid.toVector.transpose.map(xs => xs.mkString) //Norint gati vertikalius zodzius transposinsim
  349. //Gautuose atsakymuose yra tusciu tarpu simboliai. Ju nefiltruojam pries imdami zodzius, nes jie yra koordinates kuriu mes negalime prarasti
  350. val verticalWords = for(i <- 0 to verticalData.length - 1) yield {
  351. //Kiekvienas vertikalus zodis yra vienas string tiesiog jis tures savo ORIENTATION vertikalu. Todel kai turim viename stringe vertikalius zodzius
  352. //Galim susirasti ju pozicijas stringe ir taip gausim koordinates
  353. val pieces = verticalData(i).split("(?= )|(?<= )")
  354. val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
  355. val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
  356.  
  357. val objWords = for(c <- tokensWithStartIndices) yield {
  358. val cXOWord = for(xo <- 0 to c._1.length - 1 if c._1(xo) != ' ') yield {
  359. c._1(xo) match {
  360. case 'X' => X
  361. case 'O' => O
  362. }
  363. }
  364.  
  365. XOWord(Orientation.VERTICAL, cXOWord.toVector, Vec2(i, c._2))
  366. }
  367.  
  368. objWords.toVector //Array of words
  369. }.filter(x => x.word != " " && x.word.length > 1)
  370.  
  371. val horizontalWords = for(i <- 0 to grid.toVector.length - 1) yield
  372. {
  373. val pieces = grid(i).split("(?= )|(?<= )")
  374. val startIndices = pieces.scanLeft(0){ (acc, w) => acc + w.size }
  375. val tokensWithStartIndices = (pieces zip startIndices).grouped(1).map(_.head)
  376.  
  377. val objWords = for(c <- tokensWithStartIndices) yield {
  378. val cXOWord = for(xo <- 0 to c._1.length - 1 if c._1(xo) != ' ') yield {
  379. c._1(xo) match {
  380. case 'X' => X
  381. case 'O' => O
  382. }
  383. }
  384.  
  385. XOWord(Orientation.HORIZONTAL, cXOWord.toVector, Vec2(c._2, i))
  386. }
  387.  
  388. objWords.toVector //Array of words
  389. }.filter(x => x.word.length > 1)
  390.  
  391. Right(verticalWords.flatten.toVector ++ horizontalWords.flatten.toVector)
  392.  
  393. case Left(fail) => Left(fail)
  394. }
  395. }
  396. }
  397.  
  398. /*case class EnglishWord private(text: String) extends AnyVal
  399. object EnglishWord {
  400. def apply(text: String): Try[EnglishWord] = {
  401. if (isValid(text)) {
  402. Success(new EnglishWord(text))
  403. } else {
  404. Failure(new IllegalArgumentException("Invalid word: " + text))
  405. }
  406. }
  407.  
  408. def isValid(s: String): Boolean = ???
  409. }*/
  410.  
  411. def main(args: Array[String]): Unit =
  412. {
  413. val myGame = Game("Duomenys.txt")
  414.  
  415. myGame match {
  416. case Right(validGame) => validGame.printAllSolutions()
  417. case Left(errorLog) => println(errorLog)
  418. }
  419. }
  420. }
Add Comment
Please, Sign In to add comment