University stuff.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MinOppgave2.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //Oppgavetekst: Lag et spill.
  2. import java.util.ArrayList;
  3. import java.util.Scanner;
  4. import java.util.Random;
  5. import java.io.Reader;
  6. import java.io.InputStreamReader;
  7. class Util {
  8. public static int randInt(int min, int max) {
  9. return (int)(Math.random() * (max - min) + min);
  10. }
  11. public static int randInt(int min, int max, int skip) {
  12. int i;
  13. do {
  14. i = randInt(min, max);
  15. } while (i != skip);
  16. return i;
  17. }
  18. public static void exit() {
  19. try {
  20. exec("stty sane </dev/tty");
  21. } catch (Exception e) {
  22. //No need to catch the exception; we'll just exit anyways
  23. } finally {
  24. System.exit(0);
  25. }
  26. }
  27. public static void exec(String str) throws Exception {
  28. String[] cmd = {"/bin/sh", "-c", str};
  29. Runtime.getRuntime().exec(cmd).waitFor();
  30. }
  31. }
  32. class Vec2 {
  33. public double x;
  34. public double y;
  35. public Vec2(double vx, double vy) {
  36. x = vx;
  37. y = vy;
  38. }
  39. //It's sometimes useful to get the X and Y value as
  40. //rounded integers
  41. public int intX() {
  42. return (int)Math.round(x);
  43. }
  44. public int intY() {
  45. return (int)Math.round(y);
  46. }
  47. }
  48. class Entity {
  49. public String img;
  50. public String color;
  51. public Vec2 pos;
  52. public Vec2 vel;
  53. public Arena arena;
  54. public boolean dead;
  55. public Entity origin; //For entities which are spawned by other entities
  56. public Entity(double x, double y, Arena a) {
  57. pos = new Vec2(x, y);
  58. vel = new Vec2(0, 0);
  59. arena = a;
  60. dead = false;
  61. }
  62. public void update(ArrayList<Entity> entities) {}
  63. public void move() {
  64. pos.x += vel.x;
  65. pos.y += vel.y;
  66. }
  67. public void die() {
  68. dead = true;
  69. }
  70. public boolean collidesWith(Entity e) {
  71. if (dead || e.dead)
  72. return false;
  73. if (this == e)
  74. return false;
  75. return (pos.intX() == e.pos.intX() && pos.intY() == e.pos.intY());
  76. }
  77. public String identify() {
  78. return identify("entity");
  79. }
  80. public String identify(String name) {
  81. return "Entity "+img+" "+name+" at "+pos.x+", "+pos.y;
  82. }
  83. }
  84. class Player extends Entity {
  85. private boolean onGround;
  86. private static double speedOnGround = 1;
  87. private static double speedInAir = 0.3;
  88. private static double jumpSpeed = 2;
  89. private int shootCounter = 0;
  90. private int shootTimeout = 10;
  91. public Player(double x, double y, Arena a) {
  92. super(x, y, a);
  93. img = " ";
  94. color = "47";
  95. onGround = false;
  96. }
  97. @Override
  98. public String identify() {
  99. return identify("player");
  100. }
  101. public boolean shouldDieFromEnemy() {
  102. return true;
  103. }
  104. @Override
  105. public void die() {
  106. img = (char)27+"[37;41m#"+(char)27+"[0m";
  107. arena.draw();
  108. super.die();
  109. System.out.println("You died!");
  110. Util.exit();
  111. }
  112. @Override
  113. public void update(ArrayList<Entity> entities) {
  114. //Are we standing on anything?
  115. onGround = false;
  116. for (Entity e: entities) {
  117. if (e == this)
  118. continue;
  119. //For whether we're on ground or not,
  120. //we're only interested in blocks
  121. if (e instanceof Block && !onGround) {
  122. //We don't want to be stuck on ground if we're currently rising
  123. if (vel.y <= -0.5)
  124. continue;
  125. //Checking whether we collide on the X axis is simple
  126. if (e.pos.intX() != pos.intX())
  127. continue;
  128. //Checking whether we collide on the Y axis is harder
  129. if ((e.pos.intY() == pos.intY() + 1)
  130. || ((e.pos.y <= pos.y + vel.y + 1) && (e.pos.y > pos.y))) {
  131. onGround = true;
  132. pos.y = e.pos.intY() - 1;
  133. vel.y = 0;
  134. }
  135. //Die if we touch an enemy.
  136. } else if (shouldDieFromEnemy() && e instanceof Enemy) {
  137. if (collidesWith(e)) {
  138. die();
  139. }
  140. //Die if we touch a bullet
  141. } else if (e instanceof Bullet) {
  142. if (e.collidesWith(this)) {
  143. die();
  144. }
  145. }
  146. }
  147. //Decrement shoot counter
  148. if (shootCounter > 0)
  149. shootCounter -= 1;
  150. //Die if we fall through the arena floor
  151. if (pos.y > arena.dimensions.y)
  152. die();
  153. //Fall if not on ground
  154. if (!onGround)
  155. vel.y += 0.2;
  156. //Bounce off walls
  157. if (pos.intX() + vel.intX() < 0) {
  158. pos.x = 0;
  159. vel.x = -vel.x;
  160. } else if (pos.intX() + vel.intX() >= arena.dimensions.x) {
  161. pos.x = arena.dimensions.x - 1;
  162. vel.x = -vel.x;
  163. }
  164. //Turn velocity back to 0 over time
  165. vel.x *= 0.8;
  166. vel.y *= 0.9;
  167. }
  168. public void jump() {
  169. if (onGround)
  170. vel.y -= jumpSpeed;
  171. }
  172. public void moveLeft() {
  173. if (onGround)
  174. vel.x -= speedOnGround;
  175. else
  176. vel.x -= speedInAir;
  177. }
  178. public void moveRight() {
  179. if (onGround)
  180. vel.x += speedOnGround;
  181. else
  182. vel.x += speedInAir;
  183. }
  184. public void moveDown() {
  185. if (onGround)
  186. pos.y += 1;
  187. else
  188. vel.y += 0.4;
  189. }
  190. public void shoot() {
  191. if (shootCounter > 0)
  192. return;
  193. shootCounter = shootTimeout;
  194. Bullet b1 = new Bullet(pos.x, pos.y, -2, 0, this);
  195. Bullet b2 = new Bullet(pos.x - 1, pos.y, -2, 0, this);
  196. Bullet b3 = new Bullet(pos.x, pos.y, 2, 0, this);
  197. Bullet b4 = new Bullet(pos.x + 1, pos.y, 2, 0, this);
  198. arena.addEntity(b1);
  199. arena.addEntity(b2);
  200. arena.addEntity(b3);
  201. arena.addEntity(b4);
  202. }
  203. }
  204. class Enemy extends Player {
  205. private static double speedOnGround = 0.5;
  206. private enum Direction {
  207. LEFT,
  208. RIGHT
  209. }
  210. private Direction direction;
  211. public Enemy(double x, double y, Arena a) {
  212. super(x, y, a);
  213. img = " ";
  214. color = "41";
  215. //Randomly start walking in a direction
  216. if (Util.randInt(0, 1) == 0)
  217. direction = Direction.LEFT;
  218. else
  219. direction = Direction.RIGHT;
  220. }
  221. @Override
  222. public String identify() {
  223. return identify("enemy");
  224. }
  225. @Override
  226. public boolean shouldDieFromEnemy() {
  227. return false;
  228. }
  229. @Override
  230. public void die() {
  231. dead = true;
  232. }
  233. @Override
  234. public void update(ArrayList<Entity> entities) {
  235. //Randomly decide to change direction
  236. if (Util.randInt(0, 7) == 0) {
  237. if (direction == Direction.LEFT)
  238. direction = Direction.RIGHT;
  239. else
  240. direction = Direction.LEFT;
  241. }
  242. //Randomly decide to jump
  243. if (Util.randInt(0, 23) == 0) {
  244. jump();
  245. }
  246. //Randomly shoot
  247. if (Util.randInt(0, 20) == 0) {
  248. shoot();
  249. }
  250. //Change direction if we get too close to the wall
  251. if (direction == Direction.LEFT && pos.x - 5 < 0)
  252. direction = Direction.RIGHT;
  253. else if (direction == Direction.RIGHT && pos.x + 5 > arena.dimensions.x)
  254. direction = Direction.LEFT;
  255. switch (direction) {
  256. case LEFT:
  257. moveLeft();
  258. break;
  259. case RIGHT:
  260. moveRight();
  261. break;
  262. }
  263. super.update(entities);
  264. }
  265. }
  266. class Bullet extends Entity {
  267. public Entity origin;
  268. public String originalImg;
  269. public String originalColor;
  270. private boolean dieNext = false;
  271. public Bullet(double x, double y, double vx, double vy, Entity o) {
  272. super(x, y, o.arena);
  273. originalImg = "*";
  274. originalColor = o.color.replace("4", "3")+";1";
  275. img = originalImg;
  276. color = originalColor;
  277. vel.x = vx;
  278. vel.y = vy;
  279. origin = o;
  280. }
  281. @Override
  282. public String identify() {
  283. return identify("bullet (origin: "+origin.identify()+")");
  284. }
  285. @Override
  286. public void update(ArrayList<Entity> entities) {
  287. if (dieNext) {
  288. die();
  289. return;
  290. }
  291. Entity collision = null;
  292. for (Entity e: entities) {
  293. if (e.collidesWith(this))
  294. collision = e;
  295. //The bullet should die the next tick if it hits anything
  296. if (e != this && !(e instanceof Bullet) && collidesWith(e)) {
  297. dieNext = true;
  298. }
  299. }
  300. //If the bullet has hit anything, we want it to look like what it hit
  301. if (collision != null) {
  302. img = collision.img;
  303. color = collision.color;
  304. } else {
  305. img = originalImg;
  306. color = originalColor;
  307. }
  308. }
  309. @Override
  310. public boolean collidesWith(Entity e) {
  311. if (e == origin)
  312. return false;
  313. else
  314. return super.collidesWith(e);
  315. }
  316. }
  317. class Block extends Entity {
  318. public Block(int x, int y, Arena a) {
  319. super(x, y, a);
  320. img = " ";
  321. color = "42";
  322. }
  323. @Override
  324. public String identify() {
  325. return identify("block");
  326. }
  327. }
  328. class Arena {
  329. public Vec2 dimensions;
  330. public ArrayList<Entity> entities;
  331. private final String ANSI_CLS = "\u001b[2J";
  332. private final String ANSI_HOME = "\u001b[H";
  333. public Arena(int width, int height) {
  334. dimensions = new Vec2(width, height);
  335. entities = new ArrayList<Entity>();
  336. }
  337. public void addEntity(Entity e) {
  338. entities.add(e);
  339. }
  340. public void update() {
  341. //Move entities
  342. for (int i = entities.size() - 1; i >= 0; --i) {
  343. Entity e = entities.get(i);
  344. if (e.dead) {
  345. entities.remove(i);
  346. } else {
  347. e.move();
  348. }
  349. }
  350. //Update entities
  351. boolean enemiesLeft = false;
  352. for (int i = entities.size() - 1; i >= 0; --i) {
  353. Entity e = entities.get(i);
  354. e.update(entities);
  355. if (e instanceof Enemy)
  356. enemiesLeft = true;
  357. }
  358. if (!enemiesLeft) {
  359. System.out.println("No enemies left! You win!");
  360. Util.exit();
  361. }
  362. }
  363. public void draw() {
  364. String str = "\r\n\r\n\r\n";
  365. //Create draw list
  366. String[][] drawList = new String[dimensions.intX()][dimensions.intY()];
  367. for (Entity e: entities) {
  368. if (!e.dead
  369. && e.pos.x >= 0 && e.pos.intX() < dimensions.intX()
  370. && e.pos.y >= 0 && e.pos.intY() < dimensions.intY()) {
  371. drawList[e.pos.intX()][e.pos.intY()] =
  372. (char)27+"["+e.color+"m"+e.img+(char)27+"[0m";
  373. }
  374. }
  375. //Create the horizontal line at the top and bottom of the arena
  376. String horLine = "";
  377. for (int i = 0; i < dimensions.x; ++i) {
  378. horLine += "-";
  379. }
  380. //Append horizontal line to top
  381. str += " "+horLine+"\r\n";
  382. //Draw entities
  383. for (int y = 0; y < dimensions.y; ++y) {
  384. str += "|";
  385. for (int x = 0; x < dimensions.x; ++x) {
  386. String img = drawList[x][y];
  387. if (img == null)
  388. str += " ";
  389. else
  390. str += img;
  391. }
  392. str += "|\r\n";
  393. }
  394. //Append horizontal line to bottom
  395. str += " "+horLine+"\r\n";
  396. //Print the drawn arena
  397. //System.out.print(ANSI_CLS + ANSI_HOME + str);
  398. System.out.print(str);
  399. }
  400. public void generateWorld() {
  401. //Create a floor for the player to stand o
  402. for (int i = 0; i < dimensions.intX(); ++i) {
  403. Block b = new Block(i, dimensions.intY() - 3, this);
  404. addEntity(b);
  405. }
  406. int prevX = dimensions.intX() / 2;
  407. int prevY = dimensions.intY() - 3;
  408. //We want to spawn enemies and such after the terrain
  409. ArrayList<Entity> toAdd = new ArrayList<Entity>();
  410. //Generate the random terrain
  411. while (prevY > 0) {
  412. //Create a random coordinate for the next platform
  413. int nextX = Util.randInt(
  414. prevX - (int)(dimensions.x / 3),
  415. prevX + (int)(dimensions.x / 3)
  416. );
  417. int nextY = Util.randInt(
  418. prevY + 1,
  419. prevY - 3
  420. );
  421. //platforms should have random width
  422. int width = Util.randInt(3, 12);
  423. //We don't want to make platforms outside of the arena
  424. if (nextX < 0)
  425. nextX = 0;
  426. else if (nextX >= (int)dimensions.x - width)
  427. nextX = (int)dimensions.x - 1 - width;
  428. //Add the platform block
  429. for (int i = 0; i < width; ++i) {
  430. Block b = new Block(nextX + i, nextY, this);
  431. addEntity(b);
  432. }
  433. //Randomly decide to spawn an enemy on top
  434. if (Util.randInt(0, 3) == 0) {
  435. Enemy e = new Enemy(nextX, nextY - 1, this);
  436. toAdd.add(e);
  437. }
  438. prevX = nextX;
  439. prevY = nextY;
  440. }
  441. //Now that terrain is created, add enemies and such
  442. for (Entity e: toAdd) {
  443. addEntity(e);
  444. }
  445. }
  446. }
  447. class MinOppgave2 {
  448. private static Arena arena;
  449. static int[] KEY_EXIT = {3};
  450. static int[] KEY_ENTER = {13};
  451. static int[] KEY_LEFT = {27, 91, 68};
  452. static int[] KEY_RIGHT = {27, 91, 67};
  453. static int[] KEY_UP = {27, 91, 65};
  454. static int[] KEY_DOWN = {27, 91, 66};
  455. static int[] keyLog = new int[3];
  456. static Player player;
  457. public static void main(String[] args) throws Exception {
  458. Scanner s = new Scanner(System.in);
  459. //Get arena dimensions
  460. System.out.println("Insert arena width\r");
  461. int width = s.nextInt();
  462. System.out.println("Insert arena height\r");
  463. int height = s.nextInt();
  464. //Show controls
  465. System.out.println(" ------------------------------- ");
  466. System.out.println("| Controls: |");
  467. System.out.println("| ctrl+C: Exit |");
  468. System.out.println("| Left/Right: Move left/right |");
  469. System.out.println("| Up: Jump |");
  470. System.out.println("| Down: Descend |");
  471. System.out.println("| Enter: Shoot |");
  472. System.out.println(" ------------------------------- ");
  473. s.nextLine();
  474. s.nextLine();
  475. System.out.println("Generating world...");
  476. //Unix specific hackc to set the terminal to raw mode
  477. Util.exec("stty raw </dev/tty");
  478. //Initiate arena and generate the world
  479. arena = new Arena(width, height);
  480. arena.generateWorld();
  481. //Create our player
  482. player = new Player(width / 2, height - 5, arena);
  483. arena.addEntity(player);
  484. //Game loop
  485. arena.update();
  486. arena.draw();
  487. Reader r = new InputStreamReader(System.in);
  488. while (true) {
  489. logKey(r.read());
  490. if (wasKeyPressed(KEY_EXIT)) {
  491. Util.exit();
  492. } else if (wasKeyPressed(KEY_LEFT)) {
  493. player.moveLeft();
  494. } else if (wasKeyPressed(KEY_RIGHT)) {
  495. player.moveRight();
  496. } else if (wasKeyPressed(KEY_UP)) {
  497. player.jump();
  498. } else if (wasKeyPressed(KEY_ENTER)) {
  499. player.shoot();
  500. } else if (wasKeyPressed(KEY_DOWN)) {
  501. player.moveDown();
  502. } else {
  503. //Don't do anything if an unbound key was pressed;
  504. //just redraw the a rena to get rid of garbage characters
  505. arena.draw();
  506. continue;
  507. }
  508. arena.update();
  509. arena.draw();
  510. }
  511. }
  512. private static void logKey(int c) {
  513. for (int i = 0; i < keyLog.length - 1; ++i) {
  514. keyLog[i] = keyLog[i + 1];
  515. }
  516. keyLog[keyLog.length - 1] = c;
  517. }
  518. private static boolean wasKeyPressed(int[] key) {
  519. int offset = (keyLog.length - key.length);
  520. for (int i = offset; i < keyLog.length; ++i) {
  521. if (key[i - offset] != keyLog[i])
  522. return false;
  523. }
  524. return true;
  525. }
  526. }