├── CHANGELOG ├── README.md ├── .gitignore ├── favicon.ico ├── src ├── api │ ├── .vscode │ │ └── settings.json │ ├── .gitignore │ ├── handler.ts │ ├── events │ │ └── basic.json │ ├── package.json │ ├── kitty │ │ └── getKittyRecommendations.ts │ └── serverless.yml ├── index.d.ts ├── index.tsx ├── utils.ts ├── __tests__ │ └── App.test.tsx ├── components │ ├── Genes.ts │ ├── Cryptokitty.tsx │ └── About.tsx ├── cattributes │ ├── colors.ts │ ├── eye │ │ ├── googly.svg │ │ ├── fabulous.svg │ │ ├── simple.svg │ │ ├── raisedbrow.svg │ │ ├── crazy.svg │ │ ├── thicccbrowz.svg │ │ ├── otaku.svg │ │ └── wingtips.svg │ ├── mouth │ │ ├── soserious.svg │ │ ├── gerbil.svg │ │ ├── happygokitty.svg │ │ ├── whixtensions.svg │ │ ├── saycheese.svg │ │ ├── pouty.svg │ │ ├── tongue.svg │ │ ├── dali.svg │ │ └── beard.svg │ └── body │ │ ├── laperm-totesbasic.svg │ │ ├── laperm-spock.svg │ │ ├── laperm-calicool.svg │ │ └── munchkin-totesbasic.svg └── App.tsx ├── tslint.json ├── tsconfig.json ├── webpack.config.js ├── index.html └── package.json /CHANGELOG: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | SECRETS -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achadha235/cryptokitty-designer/HEAD/favicon.ico -------------------------------------------------------------------------------- /src/api/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.enable": false, 3 | "jshint.enable": false, 4 | "tslint.enable": false 5 | } -------------------------------------------------------------------------------- /src/api/.gitignore: -------------------------------------------------------------------------------- 1 | # package directories 2 | node_modules 3 | jspm_packages 4 | 5 | # Serverless directories 6 | .serverless -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.json" { 2 | const value: any; 3 | export default value; 4 | } 5 | 6 | declare module require {} -------------------------------------------------------------------------------- /src/api/handler.ts: -------------------------------------------------------------------------------- 1 | import getKittyRecommendations from './kitty/getKittyRecommendations'; 2 | module.exports.getKittyRecommendations = getKittyRecommendations; 3 | -------------------------------------------------------------------------------- /src/api/events/basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "method": "GET", 3 | "headers": { 4 | "host": "localhost:8000", 5 | "user-agent": "curl/7.49.1", 6 | "accept": "*/*" 7 | }, 8 | "body": {}, 9 | "path": {}, 10 | "query": { 11 | "foo": "bar" 12 | } 13 | } -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import * as ReactDOM from 'react-dom'; 3 | import { HashRouter } from 'react-router-dom'; 4 | import { App } from './App'; 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('app') 10 | ); 11 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warning", 3 | "extends": [ 4 | "tslint:recommended", 5 | "tslint-react" 6 | ], 7 | "jsRules": {}, 8 | "rules": { 9 | "trailing-comma": false, 10 | "quotemark": [true, "single", "avoid-escape", "avoid-template"], 11 | "indent": [true, "tabs", 2] 12 | }, 13 | "rulesDirectory": [] 14 | } 15 | -------------------------------------------------------------------------------- /src/api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "api", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "Abhishek Chadha", 6 | "license": "MIT", 7 | "devDependencies": { 8 | "serverless": "^1.24.1", 9 | "serverless-plugin-typescript": "^1.1.3" 10 | }, 11 | "dependencies": { 12 | "cryptokitties-contrib": "^0.1.0", 13 | "jsdom": "^11.5.1", 14 | "lodash": "^4.17.4", 15 | "node-fetch": "^1.7.3" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | export const isNull = (v: any) => v === undefined || v === null; 3 | export const isNonNull = (v: any) => !isNull(v); 4 | export const randomEnumValue = (v: any) => { 5 | const keys = Object.keys({...v}); 6 | const randInt = _.random(0, keys.length - 1); 7 | return v[keys[randInt]]; 8 | }; 9 | 10 | export const randomKey = (v: any) => { 11 | const keys = Object.keys({...v}); 12 | const randInt = _.random(0, keys.length - 1); 13 | return keys[randInt]; 14 | }; 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "outDir": "./dist/", 5 | "sourceMap": true, 6 | "noImplicitAny": false, 7 | "allowSyntheticDefaultImports": true, 8 | "module": "commonjs", 9 | "target": "es5", 10 | "jsx": "react", 11 | "lib": ["es2015", "dom"], 12 | "types": [ 13 | "jest" 14 | ], 15 | "externals": { 16 | "react": "React", 17 | "react-dom": "ReactDOM" 18 | } 19 | }, 20 | "include": [ 21 | "./src/**/*" 22 | ] 23 | } -------------------------------------------------------------------------------- /src/__tests__/App.test.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Basic render test 3 | */ 4 | 5 | import * as React from 'react'; 6 | import * as ShallowRenderer from 'react-test-renderer/shallow'; 7 | import { App } from '../App'; 8 | 9 | let renderer: ShallowRenderer.ShallowRenderer; 10 | 11 | beforeAll(() => { 12 | renderer = ShallowRenderer.createRenderer(); 13 | }); 14 | 15 | describe('Client basics', () => { 16 | it('App should render', () => { 17 | renderer.render(, null); 18 | const result: {} = renderer.getRenderOutput(); 19 | expect(result).toBeTruthy(); 20 | expect.assertions(1); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/components/Genes.ts: -------------------------------------------------------------------------------- 1 | // tslint:disable:forin 2 | import { BodyType, EyeType, MouthType, PatternType } from './Cryptokitty'; 3 | let map = null; 4 | let initialized = false; 5 | export const Genes = async () => { 6 | if (initialized === true) { 7 | return map; 8 | } 9 | map = {}; 10 | for (const b in BodyType) { 11 | for (const p in PatternType) { 12 | const svg = await fetch(`src/cattributes/body/${b}-${p}.svg`); 13 | map[`${b}-${p}`] = await svg.text(); 14 | } 15 | } 16 | 17 | for (const et in EyeType) { 18 | const svg = await fetch(`src/cattributes/eye/${et}.svg`); 19 | map[`${et}`] = await svg.text(); 20 | } 21 | 22 | for (const mt in MouthType) { 23 | const svg = await fetch(`src/cattributes/mouth/${mt}.svg`); 24 | map[`${mt}`] = await svg.text(); 25 | } 26 | initialized = true; 27 | return map; 28 | }; 29 | -------------------------------------------------------------------------------- /src/cattributes/colors.ts: -------------------------------------------------------------------------------- 1 | export const Primary = { 2 | mauveover: '#ded0ee', 3 | cloudwhite: '#ffffff', 4 | salmon: '#f4a792', 5 | shadowgrey: '#b1b1be', 6 | orangesoda: '#f7bc56', 7 | aquamarine: '#add5d2', 8 | greymatter: '#d1dadf', 9 | oldlace: '#ffebe9', 10 | cottoncandy: '#ecd1eb' 11 | }; 12 | 13 | export const Secondary = { 14 | peach: '#f9cfad', 15 | bloodred: '#ff7a7a', 16 | emeraldgreen: '#8be179', 17 | granitegrey: '#b1aeb9', 18 | kittencream: '#f7ebda', 19 | }; 20 | 21 | export const Tertiary = { 22 | barkbrown: '#886662', 23 | cerulian: '#385877', 24 | scarlet: '#ea5f5a', 25 | skyblue: '#83d5ff', 26 | coffee: '#756650', 27 | royalpurple: '#cf5be8', 28 | lemonade: '#ffef85', 29 | swampgreen: '#44e192', 30 | chocolate: '#c47e33', 31 | royalblue: '#5b6ee8', 32 | wolfgrey: '#737184' 33 | }; 34 | 35 | export const EyeColor = { 36 | gold: '#fcdf35', 37 | bubblegum: '#ef52d1', 38 | limegreen: '#aef72f', 39 | chestnut: '#a56429', 40 | topaz: '#0ba09c', 41 | mintgreen: '#43edac', 42 | strawberry: '#ef4b62', 43 | sizzurp: '#7c40ff', 44 | }; 45 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import { Grid, Input, Label, Menu } from 'semantic-ui-react'; 4 | import { Container } from 'semantic-ui-react'; 5 | import { About } from './components/About'; 6 | 7 | // tslint:disable-next-line:no-var-requires 8 | export class App extends React.Component<{}, {}> { 9 | 10 | public constructor(props) { 11 | super(props); 12 | } 13 | 14 | public render() { 15 | return ( 16 | 17 | 18 | 19 | 20 |

21 | CryptoKitty Designer 22 |

Make the kitty of your dreams

23 |

24 | Kittens and ETH appreciated @ 25 | 26 |

27 |
28 |
29 | 30 | 31 | 32 | 33 | 34 |
35 |
36 | ); 37 | } 38 | } 39 | 40 | const style = { 41 | }; 42 | -------------------------------------------------------------------------------- /src/cattributes/eye/googly.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: "./src/index.tsx", 3 | output: { 4 | filename: "bundle.js", 5 | path: __dirname + "/dist" 6 | }, 7 | 8 | // Enable sourcemaps for debugging webpack's output. 9 | devtool: "source-map", 10 | 11 | resolve: { 12 | // Add '.ts' and '.tsx' as resolvable extensions. 13 | extensions: [".ts", ".tsx", ".js", ".json"] 14 | }, 15 | 16 | module: { 17 | rules: [ 18 | // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'. 19 | { test: /\.tsx?$/, loader: "awesome-typescript-loader" }, 20 | 21 | // All output '.js' files will have any sourcemaps re-processed by 'source-map-loader'. 22 | { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }, 23 | 24 | // All css files will go through the webpack css-loader 25 | { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] } 26 | ] 27 | }, 28 | 29 | // When importing a module whose path matches one of the following, just 30 | // assume a corresponding global variable exists and use that instead. 31 | // This is important because it allows us to avoid bundling all of our 32 | // dependencies, which allows browsers to cache those libraries between builds. 33 | externals: { 34 | "react": "React", 35 | "react-dom": "ReactDOM", 36 | "firebase": "firebase" 37 | }, 38 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | CryptoKitty and Me 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | Star 24 |
25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cryptokitty-designer", 3 | "version": "1.0.0", 4 | "description": "Build designer cryptokitties", 5 | "main": "index.js", 6 | "author": "Abhishek Chadha", 7 | "license": "MIT", 8 | "private": true, 9 | "scripts": { 10 | "start": "webpack-dev-server", 11 | "build": "webpack" 12 | }, 13 | "dependencies": { 14 | "cryptokitties-contrib": "^0.1.0", 15 | "jest": "^21.2.1", 16 | "jsdom": "^11.5.1", 17 | "lodash": "^4.17.4", 18 | "node-fetch": "^1.7.3", 19 | "puppeteer": "^0.13.0", 20 | "react": "^16.0.0", 21 | "react-dom": "^16.0.0", 22 | "react-router-dom": "^4.2.2", 23 | "semantic-ui-react": "^0.75.1" 24 | }, 25 | "devDependencies": { 26 | "@types/jest": "^21.1.6", 27 | "@types/react": "^16.0.19", 28 | "@types/react-dom": "^16.0.2", 29 | "@types/react-router-dom": "^4.2.0", 30 | "@types/react-test-renderer": "^16.0.0", 31 | "awesome-typescript-loader": "^3.2.3", 32 | "css-loader": "^0.28.7", 33 | "react-test-renderer": "^16.1.1", 34 | "serverless-offline": "^3.16.0", 35 | "serverless-plugin-typescript": "^1.1.3", 36 | "serverless-webpack": "^4.1.0", 37 | "source-map-loader": "^0.2.3", 38 | "style-loader": "^0.19.0", 39 | "ts-jest": "^21.2.2", 40 | "ts-loader": "^3.2.0", 41 | "tslint": "^5.8.0", 42 | "tslint-react": "^3.2.0", 43 | "tslint-react-recommended": "^1.0.15", 44 | "typescript": "^2.5.3", 45 | "webpack": "^3.8.1", 46 | "webpack-dev-server": "^2.9.3" 47 | }, 48 | "jest": { 49 | "transform": { 50 | ".(ts|tsx)": "/node_modules/ts-jest/preprocessor.js" 51 | }, 52 | "testRegex": "(/__tests__/.*|\\.(test|spec))\\.(ts|tsx|js)$", 53 | "moduleFileExtensions": [ 54 | "ts", 55 | "tsx", 56 | "js", 57 | "json" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/cattributes/eye/fabulous.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/cattributes/eye/simple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/api/kitty/getKittyRecommendations.ts: -------------------------------------------------------------------------------- 1 | import * as ck from 'cryptokitties-contrib'; 2 | import { JSDOM } from 'jsdom'; 3 | import * as _ from 'lodash'; 4 | import * as fetch from 'node-fetch'; 5 | 6 | export default async function(event, context, callback) { 7 | const matron = event.queryStringParameters.matron; 8 | const sire = event.queryStringParameters.sire; 9 | const result = await kittyRecommendation(matron, [sire]); 10 | const response = { 11 | body: JSON.stringify({ 12 | input: { matron, sire }, 13 | result, 14 | }), 15 | statusCode: 200, 16 | }; 17 | callback(null, response); 18 | } 19 | 20 | async function kittyRecommendation(owned, kittys) { 21 | const kittenPrices = []; 22 | for (const kitty of kittys) { 23 | const result = await expectedPriceOfKittens(owned, kitty); 24 | kittenPrices.push({ ...result, id: kitty }); 25 | } 26 | const sorted = _.sortBy(kittenPrices, 'averagePriceOfKitten'); 27 | return sorted; 28 | } 29 | 30 | async function getKittyGenomePrediction(kitty1, kitty2) { 31 | if (typeof kitty1 !== 'string') { kitty1 = String(kitty1); } 32 | if (typeof kitty2 !== 'string') { kitty2 = String(kitty2); } 33 | const kittyGenomeServiceUrl = 'http://www.kitty.services/api'; 34 | const res = await fetch(`${kittyGenomeServiceUrl}/gene`, { 35 | body: JSON.stringify( [kitty1, kitty2]), 36 | headers: { 37 | 'Accept': 'application/json', 38 | 'Content-Type': 'application/json;charset=UTF-8', 39 | 'Origin': 'http://www.kitty.services', 40 | }, 41 | method: 'POST', 42 | }); 43 | return res.json(); 44 | } 45 | 46 | async function kittiesOnAuction() { 47 | return _.map((await ck.listAuctions()), (k) => k.id) 48 | } 49 | 50 | async function getCattributes() { 51 | const kittyGenePriceIndex = `https://cryptokittydex.com/cattributes`; 52 | const res = await fetch(`${kittyGenePriceIndex}`); 53 | const html = await res.text(); 54 | const dom = new JSDOM(html); 55 | const cattributeInfo = dom.window.document.querySelectorAll('.cattribute-info-layer'); 56 | const result = {}; 57 | _.map(cattributeInfo, (c) => { 58 | result[c.querySelector('strong').innerHTML] = { 59 | population: parseInt(c.innerHTML.split('\n')[2].replace('kitties', '').replace(/,/g, '').trim(), 10), 60 | price: parseFloat(c.querySelector('span[data-title="Average price paid for a kitty possessing this cattribute"]') 61 | .innerHTML.replace('~', '').replace('ETH', '').trim()), 62 | }; 63 | }); 64 | return result; 65 | } 66 | 67 | async function expectedPriceOfKittens(kitty1, kitty2) { 68 | const cattributeProbabilities = await getKittyGenomePrediction(kitty1, kitty2); 69 | const cattributes = await getCattributes(); 70 | const fancyPrice = 2; 71 | const cattributeProbsSum = _.reduce(cattributeProbabilities.results, (a, c) => a + c[1], 0); 72 | const normalizedCattributeProbs = _.map(cattributeProbabilities.results, (c) => [ c[0], c[1] / cattributeProbsSum]); 73 | let avgPriceOfKitten = 0; 74 | // tslint:disable-next-line:prefer-for-of 75 | for (let i = 0; i < normalizedCattributeProbs.length; i++) { 76 | if (normalizedCattributeProbs[i][0] !== 'fancy') { 77 | avgPriceOfKitten = 78 | avgPriceOfKitten + cattributes[normalizedCattributeProbs[i][0]].price * normalizedCattributeProbs[i][1]; 79 | } 80 | } 81 | return { 82 | averagePriceOfKitten: avgPriceOfKitten, 83 | results: cattributeProbabilities.results 84 | }; 85 | } 86 | -------------------------------------------------------------------------------- /src/cattributes/eye/raisedbrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/api/serverless.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Serverless! 2 | # 3 | # This file is the main config file for your service. 4 | # It's very minimal at this point and uses default values. 5 | # You can always add more config options for more control. 6 | # We've included some commented out config examples here. 7 | # Just uncomment any of them to get that config option. 8 | # 9 | # For full config options, check the docs: 10 | # docs.serverless.com 11 | # 12 | # Happy Coding! 13 | 14 | service: cryptokittyandme 15 | 16 | # You can pin your service to only deploy with a specific Serverless version 17 | # Check out our docs for more details 18 | # frameworkVersion: "=X.X.X" 19 | plugins: 20 | - serverless-plugin-typescript 21 | - serverless-offline 22 | provider: 23 | name: aws 24 | runtime: nodejs6.10 25 | 26 | functions: 27 | getKittyRecommendations: 28 | handler: handler.getKittyRecommendations 29 | events: 30 | - http: 31 | path: getKittyRecommendations 32 | method: get 33 | 34 | 35 | # you can overwrite defaults here 36 | # stage: dev 37 | # region: us-east-1 38 | 39 | # you can add statements to the Lambda function's IAM Role here 40 | # iamRoleStatements: 41 | # - Effect: "Allow" 42 | # Action: 43 | # - "s3:ListBucket" 44 | # Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] } 45 | # - Effect: "Allow" 46 | # Action: 47 | # - "s3:PutObject" 48 | # Resource: 49 | # Fn::Join: 50 | # - "" 51 | # - - "arn:aws:s3:::" 52 | # - "Ref" : "ServerlessDeploymentBucket" 53 | # - "/*" 54 | 55 | # you can define service wide environment variables here 56 | # environment: 57 | # variable1: value1 58 | 59 | # you can add packaging information here 60 | #package: 61 | # include: 62 | # - include-me.js 63 | # - include-me-dir/** 64 | # exclude: 65 | # - exclude-me.js 66 | # - exclude-me-dir/** 67 | 68 | 69 | # The following are a few example events you can configure 70 | # NOTE: Please make sure to change your handler code to work with those events 71 | # Check the event documentation for details 72 | # events: 73 | # - http: 74 | # path: users/create 75 | # method: get 76 | # - s3: ${env:BUCKET} 77 | # - schedule: rate(10 minutes) 78 | # - sns: greeter-topic 79 | # - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000 80 | # - alexaSkill 81 | # - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx 82 | # - iot: 83 | # sql: "SELECT * FROM 'some_topic'" 84 | # - cloudwatchEvent: 85 | # event: 86 | # source: 87 | # - "aws.ec2" 88 | # detail-type: 89 | # - "EC2 Instance State-change Notification" 90 | # detail: 91 | # state: 92 | # - pending 93 | # - cloudwatchLog: '/aws/lambda/hello' 94 | # - cognitoUserPool: 95 | # pool: MyUserPool 96 | # trigger: PreSignUp 97 | 98 | # Define function environment variables here 99 | # environment: 100 | # variable2: value2 101 | 102 | # you can add CloudFormation resource templates here 103 | #resources: 104 | # Resources: 105 | # NewResource: 106 | # Type: AWS::S3::Bucket 107 | # Properties: 108 | # BucketName: my-new-bucket 109 | # Outputs: 110 | # NewOutput: 111 | # Description: "Description for the output" 112 | # Value: "Some output value" 113 | -------------------------------------------------------------------------------- /src/cattributes/eye/crazy.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/cattributes/eye/thicccbrowz.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/cattributes/eye/otaku.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/cattributes/eye/wingtips.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /src/components/Cryptokitty.tsx: -------------------------------------------------------------------------------- 1 | import * as _ from 'lodash'; 2 | import * as React from 'react'; 3 | import { Link } from 'react-router-dom'; 4 | import { Button, Container, Divider, Grid, Header, Segment } from 'semantic-ui-react'; 5 | import * as c from '../cattributes/colors'; 6 | import { isNonNull, randomEnumValue } from '../utils'; 7 | import { Genes } from './Genes'; 8 | 9 | interface ICryptokittyFeatures { 10 | colors?: string[]; 11 | body?: BodyType; 12 | pattern?: PatternType; 13 | mouth?: MouthType; 14 | eye?: EyeType; 15 | isSpecial?: boolean; 16 | } 17 | 18 | export enum BodyType { 19 | mainecoon = 'mainecoon', 20 | cymric = 'cymric', 21 | laperm = 'laperm', 22 | munchkin = 'munchkin', 23 | sphynx = 'sphynx', 24 | ragamuffin = 'ragamuffin', 25 | himalayan = 'himalayan', 26 | chartreux = 'chartreux', 27 | } 28 | 29 | export enum PatternType { 30 | spock = 'spock', 31 | tigerpunk = 'tigerpunk', 32 | calicool = 'calicool', 33 | luckystripe = 'luckystripe', 34 | jaguar = 'jaguar', 35 | totesbasic = 'totesbasic', 36 | } 37 | 38 | export enum MouthType { 39 | whixtensions = 'whixtensions', 40 | dali = 'dali', 41 | saycheese = 'saycheese', 42 | beard = 'beard', 43 | tongue = 'tongue', 44 | happygokitty = 'happygokitty', 45 | pouty = 'pouty', 46 | soserious = 'soserious', 47 | gerbil = 'gerbil' 48 | } 49 | 50 | export enum EyeType { 51 | wingtips = 'wingtips', 52 | fabulous = 'fabulous', 53 | otaku = 'otaku', 54 | raisedbrow = 'raisedbrow', 55 | simple = 'simple', 56 | crazy = 'crazy', 57 | thicccbrowz = 'thicccbrowz', 58 | googly = 'googly', 59 | } 60 | 61 | interface ICryptokittyState { 62 | kittyImage?: string; 63 | kittyMouth?: string; 64 | kittyEye?: string; 65 | genes?: string; 66 | } 67 | 68 | export class Cryptokitty extends React.Component { 69 | static private cache = {}; 70 | constructor(props) { 71 | super(props); 72 | this.state = {}; 73 | const body = props.body; 74 | const pattern = props.pattern; 75 | const mouth = props.mouth; 76 | const eye = props.eye; 77 | 78 | const colors = props.colors; 79 | this.detectKittyColors = this.detectKittyColors.bind(this); 80 | this.render = this.render.bind(this); 81 | } 82 | 83 | public async componentWillMount() { 84 | const { body, pattern, mouth, eye } = this.props; 85 | const colors = this.props.colors || [ 86 | c.Primary.shadowgrey, c.Secondary.kittencream, c.Tertiary.royalpurple, c.EyeColor.bubblegum 87 | ]; 88 | const genes = await Genes(); 89 | this.setState({ genes }); 90 | } 91 | 92 | public async componentDidReceiveProps() { 93 | const { body, pattern, mouth, eye } = this.props; 94 | const colors = this.props.colors || [ 95 | c.Primary.shadowgrey, c.Secondary.kittencream, c.Tertiary.royalpurple, c.EyeColor.bubblegum 96 | ]; 97 | } 98 | 99 | public detectKittyColors(svgText) { 100 | const colors = [null, null, null, null]; 101 | for (const color in c.Primary) { 102 | if (svgText.indexOf(c.Primary[color]) > -1) { 103 | colors[0] = color; 104 | } 105 | } 106 | for (const color in c.Secondary) { 107 | if (svgText.indexOf(c.Secondary[color]) > -1) { 108 | colors[1] = color; 109 | } 110 | } 111 | for (const color in c.Tertiary) { 112 | if (svgText.indexOf(c.Tertiary[color]) > -1) { 113 | colors[2] = color; 114 | } 115 | } 116 | 117 | for (const color in c.EyeColor) { 118 | if (svgText.indexOf(c.EyeColor[color]) > -1) { 119 | colors[3] = color; 120 | } 121 | } 122 | 123 | return colors; 124 | } 125 | 126 | public render() { 127 | const genes = this.state.genes; 128 | if (genes === undefined) { 129 | return ; 130 | } 131 | const { body, pattern, mouth, eye, colors } = this.props; 132 | 133 | let kittyImage = genes[`${this.props.body}-${this.props.pattern}`]; 134 | let kittyMouth = genes[this.props.mouth]; 135 | let kittyEye = genes[this.props.eye]; 136 | 137 | const bodyColors = this.detectKittyColors(kittyImage); 138 | const eyeColors = this.detectKittyColors(kittyEye); 139 | const mouthColors = this.detectKittyColors(kittyMouth); 140 | 141 | if (isNonNull(bodyColors[0])) { 142 | kittyImage = kittyImage.replace(new RegExp(c.Primary[bodyColors[0]], "g"), colors[0]); 143 | } 144 | 145 | if (isNonNull(bodyColors[1])) { 146 | kittyImage = kittyImage.replace(new RegExp(c.Secondary[bodyColors[1]], "g"), colors[1]); 147 | } 148 | 149 | if (isNonNull(eyeColors[3])) { 150 | kittyEye = kittyEye.replace(new RegExp(c.EyeColor[eyeColors[3]], "g"), colors[3]); 151 | } 152 | 153 | if (isNonNull(bodyColors[2])) { 154 | kittyImage = kittyImage.replace(new RegExp(c.Tertiary[bodyColors[2]], "g"), colors[2]); 155 | } 156 | 157 | if (isNonNull(mouthColors[0])) { 158 | kittyMouth = kittyMouth.replace(new RegExp(c.Primary[mouthColors[0]], "g"), colors[0]); 159 | } 160 | // tslint:disable:jsx-no-multiline-js 161 | 162 | return ( 163 | 164 | { 165 | (kittyImage === null || kittyMouth === null || kittyEye === null ? 166 |
167 | 168 |
: 169 |
170 |
171 |
172 |
173 |
) 174 | } 175 | 176 | ); 177 | } 178 | } 179 | 180 | const styles: React.CSSProperties = { 181 | fixed: { position: 'absolute', top: 0, left: 0, height: "300px", width: "300px" } 182 | }; 183 | -------------------------------------------------------------------------------- /src/cattributes/mouth/soserious.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/cattributes/mouth/gerbil.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/cattributes/mouth/happygokitty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/components/About.tsx: -------------------------------------------------------------------------------- 1 | // tslint:disable:jsx-no-multiline-js 2 | import * as React from 'react'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | import { Button, Container, Divider, Grid, Header, Input, Segment } from 'semantic-ui-react'; 6 | import { BodyType, Cryptokitty, EyeType, MouthType, PatternType } from './Cryptokitty'; 7 | 8 | import * as c from '../cattributes/colors'; 9 | import { randomEnumValue, randomKey } from '../utils'; 10 | 11 | import * as _ from 'lodash'; 12 | export class About extends React.Component { 13 | 14 | constructor(props) { 15 | super(props); 16 | this.fieldChanged = this.fieldChanged.bind(this); 17 | this.randomKitty = this.randomKitty.bind(this); 18 | this.findKitty = this.randomKitty.bind(this); 19 | } 20 | 21 | // tslint:disable-next-line:member-ordering 22 | public state = { 23 | body: randomEnumValue(BodyType), 24 | eye: randomEnumValue(EyeType), 25 | eyeColor: randomKey(c.EyeColor), 26 | mouth: randomEnumValue(MouthType), 27 | pattern: randomEnumValue(PatternType), 28 | primary: randomKey(c.Primary), 29 | secondary: randomKey(c.Secondary), 30 | tertiary: randomKey(c.Tertiary), 31 | }; 32 | 33 | public fieldChanged(e) { 34 | this.setState({ 35 | ...this.state, 36 | [e.target.name]: e.target.value 37 | }); 38 | } 39 | 40 | public randomKitty() { 41 | this.setState({ 42 | body: randomEnumValue(BodyType), 43 | eye: randomEnumValue(EyeType), 44 | eyeColor: randomKey(c.EyeColor) 45 | mouth: randomEnumValue(MouthType), 46 | pattern: randomEnumValue(PatternType), 47 | primary: randomKey(c.Primary), 48 | secondary: randomKey(c.Secondary), 49 | tertiary: randomKey(c.Tertiary), 50 | }); 51 | } 52 | 53 | public render() { 54 | const onFieldChange = this.fieldChanged; 55 | const randomKitty = this.randomKitty; 56 | const findKitty = this.findKitty; 57 | const { body, pattern, eye, mouth, primary, secondary, tertiary, eyeColor } = this.state; 58 | const searchUrlStr = [body, pattern, eye, mouth, primary, secondary].join('%20'); 59 | const kittyFindUrl = `https://www.cryptokitties.co/marketplace/sale?search=${searchUrlStr}`; 60 | const openKittyUrl = () => { 61 | window.open(kittyFindUrl, '_blank'); 62 | }; 63 | return ( 64 | 65 | 66 | 67 | 68 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 |
84 |
85 | 86 | { 87 | _.map(Object.keys(BodyType), (k) => ( 88 |
89 |
90 | 91 | 92 |
93 |
94 | )) 95 | } 96 |
97 | 98 |
99 | 100 | { 101 | _.map(Object.keys(PatternType), (k) => ( 102 |
103 |
104 | 105 | 106 |
107 |
108 | )) 109 | } 110 |
111 | 112 |
113 | 114 | { 115 | _.map(Object.keys(EyeType), (k) => ( 116 |
117 |
118 | 119 | 120 |
121 |
122 | )) 123 | } 124 |
125 |
126 | 127 | { 128 | _.map(Object.keys(MouthType), (k) => ( 129 |
130 |
131 | 132 | 133 |
134 |
135 | )) 136 | } 137 |
138 |
139 | 140 | { 141 | _.map(Object.keys(c.Primary), (k) => ( 142 |
143 |
144 | 145 | 146 |
147 |
148 | )) 149 | } 150 |
151 |
152 | 153 | { 154 | _.map(Object.keys(c.Secondary), (k) => ( 155 |
156 |
157 | 158 | 159 |
160 |
161 | )) 162 | } 163 |
164 |
165 | 166 | { 167 | _.map(Object.keys(c.Tertiary), (k) => ( 168 |
169 |
170 | 171 | 172 |
173 |
174 | )) 175 | } 176 |
177 |
178 | 179 | { 180 | _.map(Object.keys(c.EyeColor), (k) => ( 181 |
182 |
183 | 184 | 185 |
186 |
187 | )) 188 | } 189 |
190 |
191 |
192 |
193 |
194 | ); 195 | } 196 | } 197 | 198 | const style = { 199 | }; 200 | -------------------------------------------------------------------------------- /src/cattributes/mouth/whixtensions.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/cattributes/mouth/saycheese.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/cattributes/mouth/pouty.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/cattributes/mouth/tongue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/cattributes/mouth/dali.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/cattributes/mouth/beard.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/cattributes/body/laperm-totesbasic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/cattributes/body/laperm-spock.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /src/cattributes/body/laperm-calicool.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/cattributes/body/munchkin-totesbasic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | --------------------------------------------------------------------------------